Merge master.kernel.org:/home/rmk/linux-2.6-mmc
authorLinus Torvalds <torvalds@g5.osdl.org>
Sun, 2 Apr 2006 20:32:55 +0000 (13:32 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Sun, 2 Apr 2006 20:32:55 +0000 (13:32 -0700)
* master.kernel.org:/home/rmk/linux-2.6-mmc:
  [ARM] 3457/1: i.MX: SD/MMC support for i.MX/MX1
  [ARM] 3456/1: AT91RM9200 support for 2.6 (MMC/SD driver)

446 files changed:
Documentation/DocBook/Makefile
Documentation/DocBook/kernel-api.tmpl
Documentation/acpi-hotkey.txt
Documentation/fujitsu/frv/kernel-ABI.txt
Documentation/input/joystick-parport.txt
Documentation/kernel-parameters.txt
Documentation/networking/packet_mmap.txt
Documentation/networking/tuntap.txt
Documentation/pcmcia/driver-changes.txt
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/video4linux/CARDLIST.saa7134
Documentation/video4linux/et61x251.txt [moved from Documentation/usb/et61x251.txt with 100% similarity]
Documentation/video4linux/ibmcam.txt [moved from Documentation/usb/ibmcam.txt with 99% similarity]
Documentation/video4linux/ov511.txt [moved from Documentation/usb/ov511.txt with 99% similarity]
Documentation/video4linux/se401.txt [moved from Documentation/usb/se401.txt with 100% similarity]
Documentation/video4linux/sn9c102.txt [moved from Documentation/usb/sn9c102.txt with 98% similarity]
Documentation/video4linux/stv680.txt [moved from Documentation/usb/stv680.txt with 82% similarity]
Documentation/video4linux/w9968cf.txt [moved from Documentation/usb/w9968cf.txt with 97% similarity]
Documentation/video4linux/zc0301.txt [moved from Documentation/usb/zc0301.txt with 100% similarity]
MAINTAINERS
arch/alpha/kernel/setup.c
arch/i386/kernel/crash.c
arch/i386/kernel/setup.c
arch/mips/Kconfig
arch/mips/kernel/Makefile
arch/mips/kernel/i8253.c [new file with mode: 0644]
arch/powerpc/kernel/lparcfg.c
arch/powerpc/kernel/rtas.c
arch/powerpc/kernel/setup-common.c
arch/powerpc/kernel/setup_32.c
arch/powerpc/kernel/setup_64.c
arch/powerpc/kernel/systbl.S
arch/powerpc/kernel/traps.c
arch/powerpc/mm/fault.c
arch/powerpc/platforms/83xx/mpc834x_sys.c
arch/powerpc/platforms/85xx/mpc85xx_ads.c
arch/powerpc/platforms/cell/spu_callbacks.c
arch/powerpc/platforms/cell/spufs/run.c
arch/powerpc/platforms/pseries/eeh.c
arch/powerpc/platforms/pseries/eeh_driver.c
arch/powerpc/platforms/pseries/eeh_event.c
arch/powerpc/platforms/pseries/hvCall.S
arch/powerpc/platforms/pseries/hvconsole.c
arch/powerpc/platforms/pseries/hvcserver.c
arch/powerpc/platforms/pseries/lpar.c
arch/powerpc/platforms/pseries/setup.c
arch/powerpc/platforms/pseries/vio.c
arch/powerpc/platforms/pseries/xics.c
arch/sparc/kernel/systbls.S
arch/sparc64/defconfig
arch/sparc64/kernel/smp.c
arch/sparc64/kernel/sys32.S
arch/sparc64/kernel/sys_sparc32.c
arch/sparc64/kernel/systbls.S
arch/sparc64/mm/fault.c
arch/sparc64/mm/hugetlbpage.c
block/ll_rw_blk.c
drivers/Makefile
drivers/bluetooth/bluecard_cs.c
drivers/bluetooth/bt3c_cs.c
drivers/bluetooth/btuart_cs.c
drivers/bluetooth/dtl1_cs.c
drivers/char/hvcs.c
drivers/char/keyboard.c
drivers/char/pcmcia/cm4000_cs.c
drivers/char/pcmcia/cm4040_cs.c
drivers/char/pcmcia/synclink_cs.c
drivers/char/watchdog/Kconfig
drivers/char/watchdog/Makefile
drivers/char/watchdog/at91_wdt.c [new file with mode: 0644]
drivers/char/watchdog/pcwd.c
drivers/char/watchdog/pcwd_usb.c
drivers/hwmon/hdaps.c
drivers/ide/legacy/ide-cs.c
drivers/ieee1394/sbp2.c
drivers/infiniband/Kconfig
drivers/infiniband/Makefile
drivers/infiniband/hw/ipath/Kconfig [new file with mode: 0644]
drivers/infiniband/hw/ipath/Makefile [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_common.h [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_cq.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_debug.h [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_diag.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_driver.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_eeprom.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_file_ops.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_fs.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_ht400.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_init_chip.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_intr.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_kernel.h [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_keys.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_layer.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_layer.h [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_mad.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_mr.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_pe800.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_qp.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_rc.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_registers.h [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_ruc.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_srq.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_stats.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_sysfs.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_uc.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_ud.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_user_pages.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_verbs.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_verbs.h [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_verbs_mcast.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ipath_wc_x86_64.c [new file with mode: 0644]
drivers/infiniband/hw/ipath/ips_common.h [new file with mode: 0644]
drivers/infiniband/hw/ipath/verbs_debug.h [new file with mode: 0644]
drivers/input/evbug.c
drivers/input/evdev.c
drivers/input/gameport/gameport.c
drivers/input/gameport/ns558.c
drivers/input/input.c
drivers/input/joydev.c
drivers/input/joystick/amijoy.c
drivers/input/joystick/db9.c
drivers/input/joystick/gamecon.c
drivers/input/joystick/iforce/iforce-ff.c
drivers/input/joystick/iforce/iforce-main.c
drivers/input/joystick/iforce/iforce.h
drivers/input/joystick/turbografx.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/corgikbd.c
drivers/input/keyboard/hil_kbd.c
drivers/input/keyboard/spitzkbd.c
drivers/input/misc/pcspkr.c
drivers/input/misc/uinput.c
drivers/input/mouse/hil_ptr.c
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/synaptics.c
drivers/input/mousedev.c
drivers/input/power.c
drivers/input/serio/hil_mlc.c
drivers/input/serio/i8042-x86ia64io.h
drivers/input/serio/libps2.c
drivers/input/serio/parkbd.c
drivers/input/serio/rpckbd.c
drivers/input/serio/serio.c
drivers/input/serio/serio_raw.c
drivers/input/tsdev.c
drivers/isdn/hardware/avm/avm_cs.c
drivers/isdn/hisax/avma1_cs.c
drivers/isdn/hisax/elsa_cs.c
drivers/isdn/hisax/sedlbauer_cs.c
drivers/isdn/hisax/teles_cs.c
drivers/md/dm-target.c
drivers/md/raid1.c
drivers/md/raid10.c
drivers/md/raid5.c
drivers/md/raid6main.c
drivers/media/Kconfig
drivers/media/dvb/bt8xx/Kconfig
drivers/media/dvb/dvb-core/dmxdev.c
drivers/media/dvb/dvb-core/dvb_frontend.c
drivers/media/dvb/dvb-core/dvb_frontend.h
drivers/media/dvb/dvb-usb/cxusb.c
drivers/media/dvb/dvb-usb/dtt200u.c
drivers/media/dvb/dvb-usb/dvb-usb-ids.h
drivers/media/dvb/dvb-usb/vp702x-fe.c
drivers/media/dvb/frontends/Kconfig
drivers/media/dvb/frontends/tda1004x.c
drivers/media/dvb/ttpci/av7110.c
drivers/media/dvb/ttpci/av7110_av.c
drivers/media/dvb/ttpci/budget-av.c
drivers/media/dvb/ttpci/budget-core.c
drivers/media/dvb/ttpci/budget-patch.c
drivers/media/dvb/ttpci/budget.h
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/bt8xx/bttv-driver.c
drivers/media/video/bt8xx/bttv-vbi.c
drivers/media/video/cpia.c
drivers/media/video/cpia2/cpia2.h
drivers/media/video/cx25840/cx25840-audio.c
drivers/media/video/cx25840/cx25840-core.c
drivers/media/video/cx25840/cx25840-core.h [moved from drivers/media/video/cx25840/cx25840.h with 72% similarity]
drivers/media/video/cx25840/cx25840-firmware.c
drivers/media/video/cx25840/cx25840-vbi.c
drivers/media/video/cx88/Kconfig
drivers/media/video/em28xx/em28xx-cards.c
drivers/media/video/em28xx/em28xx-video.c
drivers/media/video/et61x251/Kconfig [new file with mode: 0644]
drivers/media/video/ir-kbd-i2c.c
drivers/media/video/msp3400-driver.c
drivers/media/video/msp3400-driver.h
drivers/media/video/msp3400-kthreads.c
drivers/media/video/pwc/Kconfig [new file with mode: 0644]
drivers/media/video/saa7115.c
drivers/media/video/saa7127.c
drivers/media/video/saa7134/Kconfig
drivers/media/video/saa7134/saa7134-cards.c
drivers/media/video/saa7134/saa7134-dvb.c
drivers/media/video/saa7134/saa7134.h
drivers/media/video/sn9c102/Kconfig [new file with mode: 0644]
drivers/media/video/tuner-core.c
drivers/media/video/tvaudio.c
drivers/media/video/tveeprom.c
drivers/media/video/tvp5150.c
drivers/media/video/upd64031a.c [new file with mode: 0644]
drivers/media/video/upd64083.c [new file with mode: 0644]
drivers/media/video/usbvideo/Kconfig [new file with mode: 0644]
drivers/media/video/usbvideo/Makefile
drivers/media/video/v4l2-common.c
drivers/media/video/video-buf.c
drivers/media/video/wm8739.c [new file with mode: 0644]
drivers/media/video/zc0301/Kconfig [new file with mode: 0644]
drivers/mtd/chips/Kconfig
drivers/mtd/maps/pcmciamtd.c
drivers/net/8139cp.c
drivers/net/arcnet/arcnet.c
drivers/net/arcnet/com90xx.c
drivers/net/b44.c
drivers/net/chelsio/sge.c
drivers/net/e1000/e1000_main.c
drivers/net/eql.c
drivers/net/ibmveth.c
drivers/net/irda/sa1100_ir.c
drivers/net/ne2k-pci.c
drivers/net/ns83820.c
drivers/net/pcmcia/3c574_cs.c
drivers/net/pcmcia/3c589_cs.c
drivers/net/pcmcia/axnet_cs.c
drivers/net/pcmcia/com20020_cs.c
drivers/net/pcmcia/fmvj18x_cs.c
drivers/net/pcmcia/ibmtr_cs.c
drivers/net/pcmcia/nmclan_cs.c
drivers/net/pcmcia/pcnet_cs.c
drivers/net/pcmcia/smc91c92_cs.c
drivers/net/pcmcia/xirc2ps_cs.c
drivers/net/starfire.c
drivers/net/tg3.c
drivers/net/tokenring/abyss.c
drivers/net/tokenring/madgemc.c
drivers/net/wireless/airo_cs.c
drivers/net/wireless/atmel_cs.c
drivers/net/wireless/hostap/hostap_cs.c
drivers/net/wireless/ipw2200.c
drivers/net/wireless/netwave_cs.c
drivers/net/wireless/orinoco_cs.c
drivers/net/wireless/ray_cs.c
drivers/net/wireless/ray_cs.h
drivers/net/wireless/spectrum_cs.c
drivers/net/wireless/wavelan_cs.c
drivers/net/wireless/wavelan_cs.p.h
drivers/net/wireless/wl3501.h
drivers/net/wireless/wl3501_cs.c
drivers/net/yellowfin.c
drivers/parport/parport_cs.c
drivers/pcmcia/Kconfig
drivers/pcmcia/Makefile
drivers/pcmcia/at91_cf.c [new file with mode: 0644]
drivers/pcmcia/cistpl.c
drivers/pcmcia/cs.c
drivers/pcmcia/cs_internal.h
drivers/pcmcia/ds.c
drivers/pcmcia/ds_internal.h
drivers/pcmcia/i82092.c
drivers/pcmcia/i82365.c
drivers/pcmcia/pcmcia_compat.c [deleted file]
drivers/pcmcia/pcmcia_ioctl.c
drivers/pcmcia/pcmcia_resource.c
drivers/pcmcia/pd6729.c
drivers/pcmcia/rsrc_mgr.c
drivers/pcmcia/rsrc_nonstatic.c
drivers/pcmcia/sa1100_cerf.c
drivers/pcmcia/socket_sysfs.c
drivers/pcmcia/ti113x.h
drivers/s390/block/dasd_erp.c
drivers/s390/char/sclp_rw.c
drivers/s390/char/tape_block.c
drivers/s390/net/lcs.c
drivers/scsi/aic7xxx/Kconfig.aic7xxx
drivers/scsi/ibmvscsi/rpa_vscsi.c
drivers/scsi/pcmcia/aha152x_stub.c
drivers/scsi/pcmcia/fdomain_stub.c
drivers/scsi/pcmcia/nsp_cs.c
drivers/scsi/pcmcia/nsp_cs.h
drivers/scsi/pcmcia/qlogic_stub.c
drivers/scsi/pcmcia/sym53c500_cs.c
drivers/serial/jsm/jsm.h
drivers/serial/jsm/jsm_driver.c
drivers/serial/jsm/jsm_neo.c
drivers/serial/serial_cs.c
drivers/telephony/ixj_pcmcia.c
drivers/usb/host/sl811_cs.c
drivers/usb/input/hid-input.c
fs/direct-io.c
fs/dquot.c
fs/exec.c
fs/fcntl.c
fs/freevxfs/vxfs_olt.c
fs/hfsplus/bnode.c
fs/hfsplus/btree.c
fs/inode.c
fs/jffs2/background.c
fs/smbfs/file.c
fs/splice.c
fs/sysfs/dir.c
fs/sysfs/file.c
fs/sysfs/inode.c
fs/sysv/dir.c
fs/udf/inode.c
fs/xfs/linux-2.6/xfs_file.c
fs/xfs/linux-2.6/xfs_linux.h
fs/xfs/linux-2.6/xfs_lrw.c
fs/xfs/linux-2.6/xfs_lrw.h
fs/xfs/linux-2.6/xfs_super.c
fs/xfs/linux-2.6/xfs_vnode.h
fs/xfs/quota/xfs_qm.c
fs/xfs/quota/xfs_trans_dquot.c
fs/xfs/xfs_bmap.c
fs/xfs/xfs_bmap.h
fs/xfs/xfs_clnt.h
fs/xfs/xfs_error.h
fs/xfs/xfs_mount.c
fs/xfs/xfs_mount.h
fs/xfs/xfs_quota.h
fs/xfs/xfs_vfsops.c
fs/xfs/xfs_vnodeops.c
include/asm-arm/arch-at91rm9200/hardware.h
include/asm-powerpc/eeh.h
include/asm-powerpc/hvcall.h
include/asm-sparc/unistd.h
include/asm-sparc64/unistd.h
include/linux/fs.h
include/linux/gameport.h
include/linux/hrtimer.h
include/linux/input.h
include/linux/kbd_kern.h
include/linux/keyboard.h
include/linux/libps2.h
include/linux/netfilter/x_tables.h
include/linux/netfilter/xt_esp.h [new file with mode: 0644]
include/linux/netfilter/xt_multiport.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ip_tables.h
include/linux/netfilter_ipv4/ipt_esp.h
include/linux/netfilter_ipv4/ipt_multiport.h
include/linux/netfilter_ipv6/ip6t_esp.h
include/linux/netfilter_ipv6/ip6t_multiport.h
include/linux/pipe_fs_i.h
include/linux/serio.h
include/linux/uinput.h
include/linux/videodev2.h
include/media/cx25840.h [new file with mode: 0644]
include/media/msp3400.h
include/media/saa7115.h [new file with mode: 0644]
include/media/saa7127.h [new file with mode: 0644]
include/media/upd64031a.h [new file with mode: 0644]
include/media/upd64083.h [new file with mode: 0644]
include/net/xfrm.h
include/pcmcia/bulkmem.h
include/pcmcia/ciscode.h
include/pcmcia/cistpl.h
include/pcmcia/cs.h
include/pcmcia/ds.h
include/pcmcia/ss.h
include/sound/core.h
include/sound/pcm.h
include/sound/pcm_oss.h
ipc/shm.c
ipc/util.c
kernel/power/Kconfig
kernel/printk.c
kernel/ptrace.c
kernel/signal.c
kernel/time.c
kernel/timer.c
mm/highmem.c
mm/mmap.c
mm/page-writeback.c
mm/slab.c
mm/swap_state.c
mm/vmalloc.c
net/compat.c
net/ipv4/ah4.c
net/ipv4/esp4.c
net/ipv4/ipcomp.c
net/ipv4/netfilter/Kconfig
net/ipv4/netfilter/Makefile
net/ipv4/netfilter/ip_conntrack_netlink.c
net/ipv4/netfilter/ip_tables.c
net/ipv4/netfilter/ipt_multiport.c [deleted file]
net/ipv4/xfrm4_input.c
net/ipv4/xfrm4_tunnel.c
net/ipv6/ah6.c
net/ipv6/esp6.c
net/ipv6/ipcomp6.c
net/ipv6/netfilter/Kconfig
net/ipv6/netfilter/Makefile
net/ipv6/netfilter/ip6t_esp.c [deleted file]
net/ipv6/netfilter/ip6t_multiport.c [deleted file]
net/ipv6/xfrm6_input.c
net/ipv6/xfrm6_tunnel.c
net/netfilter/Kconfig
net/netfilter/Makefile
net/netfilter/nf_conntrack_netlink.c
net/netfilter/x_tables.c
net/netfilter/xt_esp.c [moved from net/ipv4/netfilter/ipt_esp.c with 51% similarity]
net/netfilter/xt_multiport.c [new file with mode: 0644]
net/netfilter/xt_policy.c
net/socket.c
net/xfrm/xfrm_input.c
net/xfrm/xfrm_policy.c
sound/core/Kconfig
sound/core/control.c
sound/core/control_compat.c
sound/core/init.c
sound/core/oss/pcm_oss.c
sound/core/pcm.c
sound/core/pcm_lib.c
sound/core/pcm_native.c
sound/isa/Kconfig
sound/isa/Makefile
sound/isa/adlib.c [new file with mode: 0644]
sound/isa/cmi8330.c
sound/isa/opti9xx/Makefile
sound/isa/opti9xx/miro.c [new file with mode: 0644]
sound/isa/opti9xx/miro.h [new file with mode: 0644]
sound/pci/Kconfig
sound/pci/Makefile
sound/pci/als300.c [new file with mode: 0644]
sound/pci/cs4281.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/ice1712/aureon.c
sound/pci/ice1712/ice1712.c
sound/pci/ice1712/ice1712.h
sound/pci/maestro3.c
sound/pci/pcxhr/pcxhr_core.c
sound/pci/riptide/Makefile [new file with mode: 0644]
sound/pci/riptide/riptide.c [new file with mode: 0644]
sound/pci/via82xx.c
sound/pcmcia/pdaudiocf/pdaudiocf.c
sound/pcmcia/pdaudiocf/pdaudiocf.h
sound/pcmcia/vx/vxpocket.c
sound/pcmcia/vx/vxpocket.h
sound/usb/usbmixer.c

index 7d87dd7..5a2882d 100644 (file)
@@ -2,7 +2,7 @@
 # This makefile is used to generate the kernel documentation,
 # primarily based on in-line comments in various source files.
 # See Documentation/kernel-doc-nano-HOWTO.txt for instruction in how
-# to ducument the SRC - and how to read it.
+# to document the SRC - and how to read it.
 # To add a new book the only step required is to add the book to the
 # list of DOCBOOKS.
 
index 8c9c670..ca02e04 100644 (file)
@@ -322,7 +322,6 @@ X!Earch/i386/kernel/mca.c
   <chapter id="sysfs">
      <title>The Filesystem for Exporting Kernel Objects</title>
 !Efs/sysfs/file.c
-!Efs/sysfs/dir.c
 !Efs/sysfs/symlink.c
 !Efs/sysfs/bin.c
   </chapter>
index 744f1ae..38040fa 100644 (file)
@@ -30,7 +30,7 @@ specific hotkey(event))
 echo "event_num:event_type:event_argument" > 
        /proc/acpi/hotkey/action.
 The result of the execution of this aml method is 
-attached to /proc/acpi/hotkey/poll_method, which is dnyamically
+attached to /proc/acpi/hotkey/poll_method, which is dynamically
 created.  Please use command "cat /proc/acpi/hotkey/polling_method" 
 to retrieve it.
 
index 0ed9b0a..8b0a5fc 100644 (file)
@@ -1,17 +1,19 @@
-                                =================================
-                                INTERNAL KERNEL ABI FOR FR-V ARCH
-                                =================================
-
-The internal FRV kernel ABI is not quite the same as the userspace ABI. A number of the registers
-are used for special purposed, and the ABI is not consistent between modules vs core, and MMU vs
-no-MMU.
-
-This partly stems from the fact that FRV CPUs do not have a separate supervisor stack pointer, and
-most of them do not have any scratch registers, thus requiring at least one general purpose
-register to be clobbered in such an event. Also, within the kernel core, it is possible to simply
-jump or call directly between functions using a relative offset. This cannot be extended to modules
-for the displacement is likely to be too far. Thus in modules the address of a function to call
-must be calculated in a register and then used, requiring two extra instructions.
+                       =================================
+                       INTERNAL KERNEL ABI FOR FR-V ARCH
+                       =================================
+
+The internal FRV kernel ABI is not quite the same as the userspace ABI. A
+number of the registers are used for special purposed, and the ABI is not
+consistent between modules vs core, and MMU vs no-MMU.
+
+This partly stems from the fact that FRV CPUs do not have a separate
+supervisor stack pointer, and most of them do not have any scratch
+registers, thus requiring at least one general purpose register to be
+clobbered in such an event. Also, within the kernel core, it is possible to
+simply jump or call directly between functions using a relative offset.
+This cannot be extended to modules for the displacement is likely to be too
+far. Thus in modules the address of a function to call must be calculated
+in a register and then used, requiring two extra instructions.
 
 This document has the following sections:
 
@@ -39,7 +41,8 @@ When a system call is made, the following registers are effective:
 CPU OPERATING MODES
 ===================
 
-The FR-V CPU has three basic operating modes. In order of increasing capability:
+The FR-V CPU has three basic operating modes. In order of increasing
+capability:
 
   (1) User mode.
 
@@ -47,42 +50,46 @@ The FR-V CPU has three basic operating modes. In order of increasing capability:
 
   (2) Kernel mode.
 
-      Normal kernel mode. There are many additional control registers available that may be
-      accessed in this mode, in addition to all the stuff available to user mode. This has two
-      submodes:
+      Normal kernel mode. There are many additional control registers
+      available that may be accessed in this mode, in addition to all the
+      stuff available to user mode. This has two submodes:
 
       (a) Exceptions enabled (PSR.T == 1).
 
-         Exceptions will invoke the appropriate normal kernel mode handler. On entry to the
-         handler, the PSR.T bit will be cleared.
+         Exceptions will invoke the appropriate normal kernel mode
+         handler. On entry to the handler, the PSR.T bit will be cleared.
 
       (b) Exceptions disabled (PSR.T == 0).
 
-         No exceptions or interrupts may happen. Any mandatory exceptions will cause the CPU to
-         halt unless the CPU is told to jump into debug mode instead.
+         No exceptions or interrupts may happen. Any mandatory exceptions
+         will cause the CPU to halt unless the CPU is told to jump into
+         debug mode instead.
 
   (3) Debug mode.
 
-      No exceptions may happen in this mode. Memory protection and management exceptions will be
-      flagged for later consideration, but the exception handler won't be invoked. Debugging traps
-      such as hardware breakpoints and watchpoints will be ignored. This mode is entered only by
-      debugging events obtained from the other two modes.
+      No exceptions may happen in this mode. Memory protection and
+      management exceptions will be flagged for later consideration, but
+      the exception handler won't be invoked. Debugging traps such as
+      hardware breakpoints and watchpoints will be ignored. This mode is
+      entered only by debugging events obtained from the other two modes.
 
-      All kernel mode registers may be accessed, plus a few extra debugging specific registers.
+      All kernel mode registers may be accessed, plus a few extra debugging
+      specific registers.
 
 
 =================================
 INTERNAL KERNEL-MODE REGISTER ABI
 =================================
 
-There are a number of permanent register assignments that are set up by entry.S in the exception
-prologue. Note that there is a complete set of exception prologues for each of user->kernel
-transition and kernel->kernel transition. There are also user->debug and kernel->debug mode
-transition prologues.
+There are a number of permanent register assignments that are set up by
+entry.S in the exception prologue. Note that there is a complete set of
+exception prologues for each of user->kernel transition and kernel->kernel
+transition. There are also user->debug and kernel->debug mode transition
+prologues.
 
 
        REGISTER        FLAVOUR USE
-       =============== ======= ====================================================
+       =============== ======= ==============================================
        GR1                     Supervisor stack pointer
        GR15                    Current thread info pointer
        GR16                    GP-Rel base register for small data
@@ -92,10 +99,12 @@ transition prologues.
        GR31            NOMMU   Destroyed by debug mode entry
        GR31            MMU     Destroyed by TLB miss kernel mode entry
        CCR.ICC2                Virtual interrupt disablement tracking
-       CCCR.CC3                Cleared by exception prologue (atomic op emulation)
+       CCCR.CC3                Cleared by exception prologue 
+                               (atomic op emulation)
        SCR0            MMU     See mmu-layout.txt.
        SCR1            MMU     See mmu-layout.txt.
-       SCR2            MMU     Save for EAR0 (destroyed by icache insns in debug mode)
+       SCR2            MMU     Save for EAR0 (destroyed by icache insns 
+                                              in debug mode)
        SCR3            MMU     Save for GR31 during debug exceptions
        DAMR/IAMR       NOMMU   Fixed memory protection layout.
        DAMR/IAMR       MMU     See mmu-layout.txt.
@@ -104,18 +113,21 @@ transition prologues.
 Certain registers are also used or modified across function calls:
 
        REGISTER        CALL                            RETURN
-       =============== =============================== ===============================
+       =============== =============================== ======================
        GR0             Fixed Zero                      -
        GR2             Function call frame pointer
        GR3             Special                         Preserved
        GR3-GR7         -                               Clobbered
-       GR8             Function call arg #1            Return value (or clobbered)
-       GR9             Function call arg #2            Return value MSW (or clobbered)
+       GR8             Function call arg #1            Return value 
+                                                       (or clobbered)
+       GR9             Function call arg #2            Return value MSW 
+                                                       (or clobbered)
        GR10-GR13       Function call arg #3-#6         Clobbered
        GR14            -                               Clobbered
        GR15-GR16       Special                         Preserved
        GR17-GR27       -                               Preserved
-       GR28-GR31       Special                         Only accessed explicitly
+       GR28-GR31       Special                         Only accessed 
+                                                       explicitly
        LR              Return address after CALL       Clobbered
        CCR/CCCR        -                               Mostly Clobbered
 
@@ -124,46 +136,53 @@ Certain registers are also used or modified across function calls:
 INTERNAL DEBUG-MODE REGISTER ABI
 ================================
 
-This is the same as the kernel-mode register ABI for functions calls. The difference is that in
-debug-mode there's a different stack and a different exception frame. Almost all the global
-registers from kernel-mode (including the stack pointer) may be changed.
+This is the same as the kernel-mode register ABI for functions calls. The
+difference is that in debug-mode there's a different stack and a different
+exception frame. Almost all the global registers from kernel-mode
+(including the stack pointer) may be changed.
 
        REGISTER        FLAVOUR USE
-       =============== ======= ====================================================
+       =============== ======= ==============================================
        GR1                     Debug stack pointer
        GR16                    GP-Rel base register for small data
-       GR31                    Current debug exception frame pointer (__debug_frame)
+       GR31                    Current debug exception frame pointer 
+                               (__debug_frame)
        SCR3            MMU     Saved value of GR31
 
 
-Note that debug mode is able to interfere with the kernel's emulated atomic ops, so it must be
-exceedingly careful not to do any that would interact with the main kernel in this regard. Hence
-the debug mode code (gdbstub) is almost completely self-contained. The only external code used is
-the sprintf family of functions.
+Note that debug mode is able to interfere with the kernel's emulated atomic
+ops, so it must be exceedingly careful not to do any that would interact
+with the main kernel in this regard. Hence the debug mode code (gdbstub) is
+almost completely self-contained. The only external code used is the
+sprintf family of functions.
 
-Futhermore, break.S is so complicated because single-step mode does not switch off on entry to an
-exception. That means unless manually disabled, single-stepping will blithely go on stepping into
-things like interrupts. See gdbstub.txt for more information.
+Futhermore, break.S is so complicated because single-step mode does not
+switch off on entry to an exception. That means unless manually disabled,
+single-stepping will blithely go on stepping into things like interrupts.
+See gdbstub.txt for more information.
 
 
 ==========================
 VIRTUAL INTERRUPT HANDLING
 ==========================
 
-Because accesses to the PSR is so slow, and to disable interrupts we have to access it twice (once
-to read and once to write), we don't actually disable interrupts at all if we don't have to. What
-we do instead is use the ICC2 condition code flags to note virtual disablement, such that if we
-then do take an interrupt, we note the flag, really disable interrupts, set another flag and resume
-execution at the point the interrupt happened. Setting condition flags as a side effect of an
-arithmetic or logical instruction is really fast. This use of the ICC2 only occurs within the
+Because accesses to the PSR is so slow, and to disable interrupts we have
+to access it twice (once to read and once to write), we don't actually
+disable interrupts at all if we don't have to. What we do instead is use
+the ICC2 condition code flags to note virtual disablement, such that if we
+then do take an interrupt, we note the flag, really disable interrupts, set
+another flag and resume execution at the point the interrupt happened.
+Setting condition flags as a side effect of an arithmetic or logical
+instruction is really fast. This use of the ICC2 only occurs within the
 kernel - it does not affect userspace.
 
 The flags we use are:
 
  (*) CCR.ICC2.Z [Zero flag]
 
-     Set to virtually disable interrupts, clear when interrupts are virtually enabled. Can be
-     modified by logical instructions without affecting the Carry flag.
+     Set to virtually disable interrupts, clear when interrupts are
+     virtually enabled. Can be modified by logical instructions without
+     affecting the Carry flag.
 
  (*) CCR.ICC2.C [Carry flag]
 
@@ -176,8 +195,9 @@ What happens is this:
 
        ICC2.Z is 0, ICC2.C is 1.
 
- (2) An interrupt occurs. The exception prologue examines ICC2.Z and determines that nothing needs
-     doing. This is done simply with an unlikely BEQ instruction.
+ (2) An interrupt occurs. The exception prologue examines ICC2.Z and
+     determines that nothing needs doing. This is done simply with an
+     unlikely BEQ instruction.
 
  (3) The interrupts are disabled (local_irq_disable)
 
@@ -187,48 +207,56 @@ What happens is this:
 
        ICC2.Z would be set to 0.
 
-     A TIHI #2 instruction (trap #2 if condition HI - Z==0 && C==0) would be used to trap if
-     interrupts were now virtually enabled, but physically disabled - which they're not, so the
-     trap isn't taken. The kernel would then be back to state (1).
+     A TIHI #2 instruction (trap #2 if condition HI - Z==0 && C==0) would
+     be used to trap if interrupts were now virtually enabled, but
+     physically disabled - which they're not, so the trap isn't taken. The
+     kernel would then be back to state (1).
 
- (5) An interrupt occurs. The exception prologue examines ICC2.Z and determines that the interrupt
-     shouldn't actually have happened. It jumps aside, and there disabled interrupts by setting
-     PSR.PIL to 14 and then it clears ICC2.C.
+ (5) An interrupt occurs. The exception prologue examines ICC2.Z and
+     determines that the interrupt shouldn't actually have happened. It
+     jumps aside, and there disabled interrupts by setting PSR.PIL to 14
+     and then it clears ICC2.C.
 
  (6) If interrupts were then saved and disabled again (local_irq_save):
 
-       ICC2.Z would be shifted into the save variable and masked off (giving a 1).
+       ICC2.Z would be shifted into the save variable and masked off 
+       (giving a 1).
 
-       ICC2.Z would then be set to 1 (thus unchanged), and ICC2.C would be unaffected (ie: 0).
+       ICC2.Z would then be set to 1 (thus unchanged), and ICC2.C would be
+       unaffected (ie: 0).
 
  (7) If interrupts were then restored from state (6) (local_irq_restore):
 
-       ICC2.Z would be set to indicate the result of XOR'ing the saved value (ie: 1) with 1, which
-       gives a result of 0 - thus leaving ICC2.Z set.
+       ICC2.Z would be set to indicate the result of XOR'ing the saved
+       value (ie: 1) with 1, which gives a result of 0 - thus leaving
+       ICC2.Z set.
 
        ICC2.C would remain unaffected (ie: 0).
 
-     A TIHI #2 instruction would be used to again assay the current state, but this would do
-     nothing as Z==1.
+     A TIHI #2 instruction would be used to again assay the current state,
+     but this would do nothing as Z==1.
 
  (8) If interrupts were then enabled (local_irq_enable):
 
-       ICC2.Z would be cleared. ICC2.C would be left unaffected. Both flags would now be 0.
+       ICC2.Z would be cleared. ICC2.C would be left unaffected. Both
+       flags would now be 0.
 
-     A TIHI #2 instruction again issued to assay the current state would then trap as both Z==0
-     [interrupts virtually enabled] and C==0 [interrupts really disabled] would then be true.
+     A TIHI #2 instruction again issued to assay the current state would
+     then trap as both Z==0 [interrupts virtually enabled] and C==0
+     [interrupts really disabled] would then be true.
 
- (9) The trap #2 handler would simply enable hardware interrupts (set PSR.PIL to 0), set ICC2.C to
-     1 and return.
+ (9) The trap #2 handler would simply enable hardware interrupts 
+     (set PSR.PIL to 0), set ICC2.C to 1 and return.
 
 (10) Immediately upon returning, the pending interrupt would be taken.
 
-(11) The interrupt handler would take the path of actually processing the interrupt (ICC2.Z is
-     clear, BEQ fails as per step (2)).
+(11) The interrupt handler would take the path of actually processing the
+     interrupt (ICC2.Z is clear, BEQ fails as per step (2)).
 
-(12) The interrupt handler would then set ICC2.C to 1 since hardware interrupts are definitely
-     enabled - or else the kernel wouldn't be here.
+(12) The interrupt handler would then set ICC2.C to 1 since hardware
+     interrupts are definitely enabled - or else the kernel wouldn't be here.
 
 (13) On return from the interrupt handler, things would be back to state (1).
 
-This trap (#2) is only available in kernel mode. In user mode it will result in SIGILL.
+This trap (#2) is only available in kernel mode. In user mode it will
+result in SIGILL.
index 88a011c..d537c48 100644 (file)
@@ -36,12 +36,12 @@ with them.
 
   All NES and SNES use the same synchronous serial protocol, clocked from
 the computer's side (and thus timing insensitive). To allow up to 5 NES
-and/or SNES gamepads connected to the parallel port at once, the output
-lines of the parallel port are shared, while one of 5 available input lines
-is assigned to each gamepad.
+and/or SNES gamepads and/or SNES mice connected to the parallel port at once,
+the output lines of the parallel port are shared, while one of 5 available
+input lines is assigned to each gamepad.
 
   This protocol is handled by the gamecon.c driver, so that's the one
-you'll use for NES and SNES gamepads.
+you'll use for NES, SNES gamepads and SNES mice.
 
   The main problem with PC parallel ports is that they don't have +5V power
 source on any of their pins. So, if you want a reliable source of power
@@ -106,7 +106,7 @@ A, Turbo B, Select and Start, and is connected through 5 wires, then it is
 either a NES or NES clone and will work with this connection. SNES gamepads
 also use 5 wires, but have more buttons. They will work as well, of course.
 
-Pinout for NES gamepads                 Pinout for SNES gamepads
+Pinout for NES gamepads                 Pinout for SNES gamepads and mice
 
           +----> Power                   +-----------------------\
           |                            7 | o  o  o  o |  x  x  o  | 1
@@ -454,6 +454,7 @@ uses the following kernel/module command line:
          6  | N64 pad
          7  | Sony PSX controller
          8  | Sony PSX DDR controller
+         9  | SNES mouse
 
   The exact type of the PSX controller type is autoprobed when used so
 hot swapping should work (but is not recomended).
index f8cb55c..b3a6187 100644 (file)
@@ -1,4 +1,4 @@
-February 2003             Kernel Parameters                     v2.5.59
+                          Kernel Parameters
                           ~~~~~~~~~~~~~~~~~
 
 The following is a consolidated list of the kernel parameters as implemented
@@ -17,9 +17,17 @@ are specified on the kernel command line with the module name plus
 
        usbcore.blinkenlights=1
 
-The text in square brackets at the beginning of the description states the
-restrictions on the kernel for the said kernel parameter to be valid. The
-restrictions referred to are that the relevant option is valid if:
+This document may not be entirely up to date and comprehensive. The command
+"modinfo -p ${modulename}" shows a current list of all parameters of a loadable
+module. Loadable modules, after being loaded into the running kernel, also
+reveal their parameters in /sys/module/${modulename}/parameters/. Some of these
+parameters may be changed at runtime by the command
+"echo -n ${value} > /sys/module/${modulename}/parameters/${parm}".
+
+The parameters listed below are only valid if certain kernel build options were
+enabled and if respective hardware is present. The text in square brackets at
+the beginning of each description states the restrictions within which a
+parameter is applicable:
 
        ACPI    ACPI support is enabled.
        ALSA    ALSA sound support is enabled.
@@ -1046,10 +1054,10 @@ running once the system is up.
        noltlbs         [PPC] Do not use large page/tlb entries for kernel
                        lowmem mapping on PPC40x.
 
-       nomce           [IA-32] Machine Check Exception
-
        nomca           [IA-64] Disable machine check abort handling
 
+       nomce           [IA-32] Machine Check Exception
+
        noresidual      [PPC] Don't use residual data on PReP machines.
 
        noresume        [SWSUSP] Disables resume and restores original swap
@@ -1682,20 +1690,6 @@ running once the system is up.
 
 
 ______________________________________________________________________
-Changelog:
-
-2000-06-??     Mr. Unknown
-       The last known update (for 2.4.0) - the changelog was not kept before.
-
-2002-11-24     Petr Baudis <pasky@ucw.cz>
-               Randy Dunlap <randy.dunlap@verizon.net>
-       Update for 2.5.49, description for most of the options introduced,
-       references to other documentation (C files, READMEs, ..), added S390,
-       PPC, SPARC, MTD, ALSA and OSS category. Minor corrections and
-       reformatting.
-
-2005-10-19     Randy Dunlap <rdunlap@xenotime.net>
-       Lots of typos, whitespace, some reformatting.
 
 TODO:
 
index 4fc8e98..aaf99d5 100644 (file)
@@ -254,7 +254,7 @@ and, the number of frames be
 
        <block number> * <block size> / <frame size>
 
-Suposse the following parameters, which apply for 2.6 kernel and an
+Suppose the following parameters, which apply for 2.6 kernel and an
 i386 architecture:
 
        <size-max> = 131072 bytes
index ec3d109..76750fb 100644 (file)
@@ -138,7 +138,7 @@ This means that you have to read/write IP packets when you are using tun and
 ethernet frames when using tap.
 
 5. What is the difference between BPF and TUN/TAP driver?
-BFP is an advanced packet filter. It can be attached to existing
+BPF is an advanced packet filter. It can be attached to existing
 network interface. It does not provide a virtual network interface.
 A TUN/TAP driver does provide a virtual network interface and it is possible
 to attach BPF to this interface.
index 97420f0..4739c5c 100644 (file)
@@ -1,5 +1,11 @@
 This file details changes in 2.6 which affect PCMCIA card driver authors:
 
+* New release helper (as of 2.6.17)
+   Instead of calling pcmcia_release_{configuration,io,irq,win}, all that's
+   necessary now is calling pcmcia_disable_device. As there is no valid
+   reason left to call pcmcia_release_io and pcmcia_release_irq, the
+   exports for them were removed.
+
 * Unify detach and REMOVAL event code, as well as attach and INSERTION
   code (as of 2.6.16)
        void (*remove)          (struct pcmcia_device *dev);
index 1def604..0ee2c7d 100644 (file)
@@ -120,6 +120,34 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     enable     - enable card
                - Default: enabled, for PCI and ISA PnP cards
 
+  Module snd-adlib
+  ----------------
+
+    Module for AdLib FM cards.
+
+    port       - port # for OPL chip
+
+    This module supports multiple cards. It does not support autoprobe, so
+    the port must be specified. For actual AdLib FM cards it will be 0x388.
+    Note that this card does not have PCM support and no mixer; only FM
+    synthesis.
+
+    Make sure you have "sbiload" from the alsa-tools package available and,
+    after loading the module, find out the assigned ALSA sequencer port
+    number through "sbiload -l". Example output:
+
+      Port     Client name                       Port name
+      64:0     OPL2 FM synth                     OPL2 FM Port
+
+    Load the std.sb and drums.sb patches also supplied by sbiload:
+
+      sbiload -p 64:0 std.sb drums.sb
+
+    If you use this driver to drive an OPL3, you can use std.o3 and drums.o3
+    instead. To have the card produce sound, use aplaymidi from alsa-utils:
+
+      aplaymidi -p 64:0 foo.mid
+
   Module snd-ad1816a
   ------------------
 
@@ -190,6 +218,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     The power-management is supported.
 
+  Module snd-als300
+  -----------------
+
+    Module for  Avance Logic ALS300 and ALS300+
+
+    This module supports multiple cards.
+
+    The power-management is supported.
+
   Module snd-als4000
   ------------------
 
@@ -701,6 +738,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          uniwill       3-jack
          F1734         2-jack
          lg            LG laptop (m1 express dual)
+         lg-lw         LG LW20 laptop
          test          for testing/debugging purpose, almost all controls can be
                        adjusted.  Appearing only when compiled with
                        $CONFIG_SND_DEBUG=y
@@ -1013,6 +1051,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     The power-management is supported.
 
+  Module snd-miro
+  ---------------
+
+    Module for Miro soundcards: miroSOUND PCM 1 pro, 
+                               miroSOUND PCM 12,
+                               miroSOUND PCM 20 Radio.
+
+    port       - Port # (0x530,0x604,0xe80,0xf40)
+    irq                - IRQ # (5,7,9,10,11)
+    dma1       - 1st dma # (0,1,3)
+    dma2       - 2nd dma # (0,1)
+    mpu_port   - MPU-401 port # (0x300,0x310,0x320,0x330)
+    mpu_irq    - MPU-401 irq # (5,7,9,10)
+    fm_port    - FM Port # (0x388)
+    wss                - enable WSS mode
+    ide                - enable onboard ide support
+
   Module snd-mixart
   -----------------
 
@@ -1202,6 +1257,20 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     The power-management is supported.
 
+  Module snd-riptide
+  ------------------
+
+    Module for Conexant Riptide chip
+
+      joystick_port    - Joystick port # (default: 0x200)
+      mpu_port         - MPU401 port # (default: 0x330)
+      opl3_port                - OPL3 port # (default: 0x388)
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+    You need to install the firmware file "riptide.hex" to the standard
+    firmware path (e.g. /lib/firmware).
+
   Module snd-rme32
   ----------------
 
index 8c71954..bca5090 100644 (file)
@@ -52,7 +52,7 @@
  51 -> ProVideo PV952                           [1540:9524]
  52 -> AverMedia AverTV/305                     [1461:2108]
  53 -> ASUS TV-FM 7135                          [1043:4845]
- 54 -> LifeView FlyTV Platinum FM               [5168:0214,1489:0214]
+ 54 -> LifeView FlyTV Platinum FM / Gold        [5168:0214,1489:0214,5168:0304]
  55 -> LifeView FlyDVB-T DUO                    [5168:0306]
  56 -> Avermedia AVerTV 307                     [1461:a70a]
  57 -> Avermedia AVerTV GO 007 FM               [1461:f31f]
@@ -84,7 +84,7 @@
  83 -> Terratec Cinergy 250 PCI TV              [153b:1160]
  84 -> LifeView FlyDVB Trio                     [5168:0319]
  85 -> AverTV DVB-T 777                         [1461:2c05]
- 86 -> LifeView FlyDVB-T                        [5168:0301]
+ 86 -> LifeView FlyDVB-T / Genius VideoWonder DVB-T [5168:0301,1489:0301]
  87 -> ADS Instant TV Duo Cardbus PTV331        [0331:1421]
  88 -> Tevion/KWorld DVB-T 220RF                [17de:7201]
  89 -> ELSA EX-VISION 700TV                     [1048:226c]
@@ -92,3 +92,4 @@
  91 -> AVerMedia A169 B                         [1461:7360]
  92 -> AVerMedia A169 B1                        [1461:6360]
  93 -> Medion 7134 Bridge #2                    [16be:0005]
+ 94 -> LifeView FlyDVB-T Hybrid Cardbus         [5168:3306,5168:3502]
similarity index 99%
rename from Documentation/usb/ibmcam.txt
rename to Documentation/video4linux/ibmcam.txt
index c250036..4a40a2e 100644 (file)
@@ -122,7 +122,7 @@ WHAT YOU NEED:
 - A Linux box with USB support (2.3/2.4; 2.2 w/backport may work)
 
 - A Video4Linux compatible frame grabber program such as xawtv.
-  
+
 HOW TO COMPILE THE DRIVER:
 
 You need to compile the driver only if you are a developer
similarity index 99%
rename from Documentation/usb/ov511.txt
rename to Documentation/video4linux/ov511.txt
index a7fc043..142741e 100644 (file)
@@ -9,7 +9,7 @@ INTRODUCTION:
 
 This is a driver for the OV511, a USB-only chip used in many "webcam" devices.
 Any camera using the OV511/OV511+ and the OV6620/OV7610/20/20AE should work.
-Video capture devices that use the Philips SAA7111A decoder also work. It 
+Video capture devices that use the Philips SAA7111A decoder also work. It
 supports streaming and capture of color or monochrome video via the Video4Linux
 API. Most V4L apps are compatible with it. Most resolutions with a width and
 height that are a multiple of 8 are supported.
@@ -52,15 +52,15 @@ from it:
 
        chmod 666 /dev/video
        chmod 666 /dev/video0 (if necessary)
-       
+
 Now you are ready to run a video app! Both vidcat and xawtv work well for me
 at 640x480.
-       
+
 [Using vidcat:]
 
        vidcat -s 640x480 -p c > test.jpg
        xview test.jpg
-       
+
 [Using xawtv:]
 
 From the main xawtv directory:
@@ -70,7 +70,7 @@ From the main xawtv directory:
        make
        make install
 
-Now you should be able to run xawtv. Right click for the options dialog. 
+Now you should be able to run xawtv. Right click for the options dialog.
 
 MODULE PARAMETERS:
 
@@ -286,4 +286,3 @@ Randy Dunlap, and others. Big thanks to them for their pioneering work on that
 and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and
 image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio
 Matsuoka for their work as well.
-
similarity index 98%
rename from Documentation/usb/sn9c102.txt
rename to Documentation/video4linux/sn9c102.txt
index b957bea..142920b 100644 (file)
@@ -174,7 +174,7 @@ Module parameters are listed below:
 -------------------------------------------------------------------------------
 Name:           video_nr
 Type:           short array (min = 0, max = 64)
-Syntax:         <-1|n[,...]> 
+Syntax:         <-1|n[,...]>
 Description:    Specify V4L2 minor mode number:
                 -1 = use next available
                  n = use minor number n
@@ -187,7 +187,7 @@ Default:        -1
 -------------------------------------------------------------------------------
 Name:           force_munmap
 Type:           bool array (min = 0, max = 64)
-Syntax:         <0|1[,...]> 
+Syntax:         <0|1[,...]>
 Description:    Force the application to unmap previously mapped buffer memory
                 before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not
                 all the applications support this feature. This parameter is
@@ -206,7 +206,7 @@ Default:        2
 -------------------------------------------------------------------------------
 Name:           debug
 Type:           ushort
-Syntax:         <n> 
+Syntax:         <n>
 Description:    Debugging information level, from 0 to 3:
                 0 = none (use carefully)
                 1 = critical errors
@@ -267,7 +267,7 @@ The sysfs interface also provides the "frame_header" entry, which exports the
 frame header of the most recent requested and captured video frame. The header
 is always 18-bytes long and is appended to every video frame by the SN9C10x
 controllers. As an example, this additional information can be used by the user
-application for implementing auto-exposure features via software. 
+application for implementing auto-exposure features via software.
 
 The following table describes the frame header:
 
@@ -441,7 +441,7 @@ blue pixels in one video frame. Each pixel is associated with a 8-bit long
 value and is disposed in memory according to the pattern shown below:
 
 B[0]   G[1]    B[2]    G[3]    ...   B[m-2]         G[m-1]
-G[m]   R[m+1]  G[m+2]  R[m+2]  ...   G[2m-2]        R[2m-1] 
+G[m]   R[m+1]  G[m+2]  R[m+2]  ...   G[2m-2]        R[2m-1]
 ...
 ...                                  B[(n-1)(m-2)]  G[(n-1)(m-1)]
 ...                                  G[n(m-2)]      R[n(m-1)]
@@ -472,12 +472,12 @@ The pixel reference value is calculated as follows:
 The algorithm purely describes the conversion from compressed Bayer code used
 in the SN9C10x chips to uncompressed Bayer. Additional steps are required to
 convert this to a color image (i.e. a color interpolation algorithm).
+
 The following Huffman codes have been found:
-0: +0 (relative to reference pixel value) 
+0: +0 (relative to reference pixel value)
 100: +4
 101: -4?
-1110xxxx: set absolute value to xxxx.0000 
+1110xxxx: set absolute value to xxxx.0000
 1101: +11
 1111: -11
 11001: +20
similarity index 82%
rename from Documentation/usb/stv680.txt
rename to Documentation/video4linux/stv680.txt
index 6448041..4f8946f 100644 (file)
@@ -5,15 +5,15 @@ Copyright, 2001, Kevin Sisson
 
 INTRODUCTION:
 
-STMicroelectronics produces the STV0680B chip, which comes in two 
-types, -001 and -003. The -003 version allows the recording and downloading 
-of sound clips from the camera, and allows a flash attachment. Otherwise, 
-it uses the same commands as the -001 version. Both versions support a 
-variety of SDRAM sizes and sensors, allowing for a maximum of 26 VGA or 20 
-CIF pictures. The STV0680 supports either a serial or a usb interface, and 
+STMicroelectronics produces the STV0680B chip, which comes in two
+types, -001 and -003. The -003 version allows the recording and downloading
+of sound clips from the camera, and allows a flash attachment. Otherwise,
+it uses the same commands as the -001 version. Both versions support a
+variety of SDRAM sizes and sensors, allowing for a maximum of 26 VGA or 20
+CIF pictures. The STV0680 supports either a serial or a usb interface, and
 video is possible through the usb interface.
 
-The following cameras are known to work with this driver, although any 
+The following cameras are known to work with this driver, although any
 camera with Vendor/Product codes of 0553/0202 should work:
 
 Aiptek Pencam (various models)
@@ -34,15 +34,15 @@ http://www.linux-usb.org
 MODULE OPTIONS:
 
 When the driver is compiled as a module, you can set a "swapRGB=1"
-option, if necessary, for those applications that require it 
-(such as xawtv). However, the driver should detect and set this 
+option, if necessary, for those applications that require it
+(such as xawtv). However, the driver should detect and set this
 automatically, so this option should not normally be used.
 
 
 KNOWN PROBLEMS:
 
-The driver seems to work better with the usb-ohci than the usb-uhci host 
-controller driver. 
+The driver seems to work better with the usb-ohci than the usb-uhci host
+controller driver.
 
 HELP:
 
@@ -50,6 +50,4 @@ The latest info on this driver can be found at:
 http://personal.clt.bellsouth.net/~kjsisson or at
 http://stv0680-usb.sourceforge.net
 
-Any questions to me can be send to:  kjsisson@bellsouth.net
-
-
+Any questions to me can be send to:  kjsisson@bellsouth.net
\ No newline at end of file
similarity index 97%
rename from Documentation/usb/w9968cf.txt
rename to Documentation/video4linux/w9968cf.txt
index 9d46cd0..3b704f2 100644 (file)
@@ -1,5 +1,5 @@
 
-                   W996[87]CF JPEG USB Dual Mode Camera Chip 
+                   W996[87]CF JPEG USB Dual Mode Camera Chip
                      Driver for Linux 2.6 (basic version)
                    =========================================
 
@@ -115,7 +115,7 @@ additional testing and full support, would be much appreciated.
 ======================
 For it to work properly, the driver needs kernel support for Video4Linux, USB
 and I2C, and the "ovcamchip" module for the image sensor. Make sure you are not
-actually using any external "ovcamchip" module, given that the W996[87]CF 
+actually using any external "ovcamchip" module, given that the W996[87]CF
 driver depends on the version of the module present in the official kernels.
 
 The following options of the kernel configuration file must be enabled and
@@ -197,16 +197,16 @@ Note:            The kernel must be compiled with the CONFIG_KMOD option
                  enabled for the 'ovcamchip' module to be loaded and for
                  this parameter to be present.
 -------------------------------------------------------------------------------
-Name:           simcams 
-Type:           int 
-Syntax:         <n> 
+Name:           simcams
+Type:           int
+Syntax:         <n>
 Description:    Number of cameras allowed to stream simultaneously.
                 n may vary from 0 to 32.
 Default:        32
 -------------------------------------------------------------------------------
 Name:           video_nr
 Type:           int array (min = 0, max = 32)
-Syntax:         <-1|n[,...]> 
+Syntax:         <-1|n[,...]>
 Description:    Specify V4L minor mode number.
                 -1 = use next available
                  n = use minor number n
@@ -219,7 +219,7 @@ Default:        -1
 -------------------------------------------------------------------------------
 Name:           packet_size
 Type:           int array (min = 0, max = 32)
-Syntax:         <n[,...]> 
+Syntax:         <n[,...]>
 Description:    Specify the maximum data payload size in bytes for alternate
                 settings, for each device. n is scaled between 63 and 1023.
 Default:        1023
@@ -234,7 +234,7 @@ Default:        2
 -------------------------------------------------------------------------------
 Name:           double_buffer
 Type:           bool array (min = 0, max = 32)
-Syntax:         <0|1[,...]> 
+Syntax:         <0|1[,...]>
 Description:    Hardware double buffering: 0 disabled, 1 enabled.
                 It should be enabled if you want smooth video output: if you
                 obtain out of sync. video, disable it, or try to
@@ -243,13 +243,13 @@ Default:        1 for every device.
 -------------------------------------------------------------------------------
 Name:           clamping
 Type:           bool array (min = 0, max = 32)
-Syntax:         <0|1[,...]> 
+Syntax:         <0|1[,...]>
 Description:    Video data clamping: 0 disabled, 1 enabled.
 Default:        0 for every device.
 -------------------------------------------------------------------------------
 Name:           filter_type
 Type:           int array (min = 0, max = 32)
-Syntax:         <0|1|2[,...]> 
+Syntax:         <0|1|2[,...]>
 Description:    Video filter type.
                 0 none, 1 (1-2-1) 3-tap filter, 2 (2-3-6-3-2) 5-tap filter.
                 The filter is used to reduce noise and aliasing artifacts
@@ -258,13 +258,13 @@ Default:        0 for every device.
 -------------------------------------------------------------------------------
 Name:           largeview
 Type:           bool array (min = 0, max = 32)
-Syntax:         <0|1[,...]> 
+Syntax:         <0|1[,...]>
 Description:    Large view: 0 disabled, 1 enabled.
 Default:        1 for every device.
 -------------------------------------------------------------------------------
 Name:           upscaling
 Type:           bool array (min = 0, max = 32)
-Syntax:         <0|1[,...]> 
+Syntax:         <0|1[,...]>
 Description:    Software scaling (for non-compressed video only):
                 0 disabled, 1 enabled.
                 Disable it if you have a slow CPU or you don't have enough
@@ -341,8 +341,8 @@ Default:        50 for every device.
 -------------------------------------------------------------------------------
 Name:           bandingfilter
 Type:           bool array (min = 0, max = 32)
-Syntax:         <0|1[,...]> 
-Description:    Banding filter to reduce effects of fluorescent 
+Syntax:         <0|1[,...]>
+Description:    Banding filter to reduce effects of fluorescent
                 lighting:
                 0 disabled, 1 enabled.
                 This filter tries to reduce the pattern of horizontal
@@ -374,7 +374,7 @@ Default:        0 for every device.
 -------------------------------------------------------------------------------
 Name:           monochrome
 Type:           bool array (min = 0, max = 32)
-Syntax:         <0|1[,...]> 
+Syntax:         <0|1[,...]>
 Description:    The image sensor is monochrome:
                 0 = no, 1 = yes
 Default:        0 for every device.
@@ -400,19 +400,19 @@ Default:        32768 for every device.
 -------------------------------------------------------------------------------
 Name:           contrast
 Type:           long array (min = 0, max = 32)
-Syntax:         <n[,...]> 
+Syntax:         <n[,...]>
 Description:    Set picture contrast (0-65535).
 Default:        50000 for every device.
 -------------------------------------------------------------------------------
 Name:           whiteness
 Type:           long array (min = 0, max = 32)
-Syntax:         <n[,...]> 
+Syntax:         <n[,...]>
 Description:    Set picture whiteness (0-65535).
 Default:        32768 for every device.
 -------------------------------------------------------------------------------
 Name:           debug
 Type:           int
-Syntax:         <n> 
+Syntax:         <n>
 Description:    Debugging information level, from 0 to 6:
                 0 = none (use carefully)
                 1 = critical errors
index c946581..f97657b 100644 (file)
@@ -1451,6 +1451,12 @@ P:       Juanjo Ciarlante
 M:     jjciarla@raiz.uncu.edu.ar
 S:     Maintained
 
+IPATH DRIVER:
+P:     Bryan O'Sullivan
+M:     support@pathscale.com
+L:     openib-general@openib.org
+S:     Supported
+
 IPX NETWORK LAYER
 P:     Arnaldo Carvalho de Melo
 M:     acme@conectiva.com.br
index dd87696..a15e18a 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/init.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
+#include <linux/platform_device.h>
 #include <linux/bootmem.h>
 #include <linux/pci.h>
 #include <linux/seq_file.h>
@@ -1478,3 +1479,20 @@ alpha_panic_event(struct notifier_block *this, unsigned long event, void *ptr)
 #endif
         return NOTIFY_DONE;
 }
+
+static __init int add_pcspkr(void)
+{
+       struct platform_device *pd;
+       int ret;
+
+       pd = platform_device_alloc("pcspkr", -1);
+       if (!pd)
+               return -ENOMEM;
+
+       ret = platform_device_add(pd);
+       if (ret)
+               platform_device_put(pd);
+
+       return ret;
+}
+device_initcall(add_pcspkr);
index e3c5fca..2b0cfce 100644 (file)
@@ -69,7 +69,7 @@ static void crash_save_this_cpu(struct pt_regs *regs, int cpu)
         * for the data I pass, and I need tags
         * on the data to indicate what information I have
         * squirrelled away.  ELF notes happen to provide
-        * all of that that no need to invent something new.
+        * all of that, so there is no need to invent something new.
         */
        buf = (u32*)per_cpu_ptr(crash_notes, cpu);
        if (!buf)
index 8c08660..eacc3f0 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/initrd.h>
 #include <linux/bootmem.h>
 #include <linux/seq_file.h>
+#include <linux/platform_device.h>
 #include <linux/console.h>
 #include <linux/mca.h>
 #include <linux/root_dev.h>
@@ -1547,6 +1548,23 @@ void __init setup_arch(char **cmdline_p)
 #endif
 }
 
+static __init int add_pcspkr(void)
+{
+       struct platform_device *pd;
+       int ret;
+
+       pd = platform_device_alloc("pcspkr", -1);
+       if (!pd)
+               return -ENOMEM;
+
+       ret = platform_device_add(pd);
+       if (ret)
+               platform_device_put(pd);
+
+       return ret;
+}
+device_initcall(add_pcspkr);
+
 #include "setup_arch_post.h"
 /*
  * Local Variables:
index 5080ea1..e15709c 100644 (file)
@@ -233,6 +233,7 @@ config MACH_JAZZ
        select ARC32
        select ARCH_MAY_HAVE_PC_FDC
        select GENERIC_ISA_DMA
+       select I8253
        select I8259
        select ISA
        select SYS_HAS_CPU_R4X00
@@ -530,6 +531,7 @@ config QEMU
        select DMA_COHERENT
        select GENERIC_ISA_DMA
        select HAVE_STD_PC_SERIAL_PORT
+       select I8253
        select I8259
        select ISA
        select SWAP_IO_SPACE
@@ -714,6 +716,7 @@ config SNI_RM200_PCI
        select HAVE_STD_PC_SERIAL_PORT
        select HW_HAS_EISA
        select HW_HAS_PCI
+       select I8253
        select I8259
        select ISA
        select SYS_HAS_CPU_R4X00
@@ -1721,6 +1724,9 @@ config MMU
        bool
        default y
 
+config I8253
+       bool
+
 source "drivers/pcmcia/Kconfig"
 
 source "drivers/pci/hotplug/Kconfig"
index f36c4f2..309d54c 100644 (file)
@@ -59,6 +59,8 @@ obj-$(CONFIG_PROC_FS)         += proc.o
 
 obj-$(CONFIG_64BIT)            += cpu-bugs64.o
 
+obj-$(CONFIG_I8253)            += i8253.o
+
 CFLAGS_cpu-bugs64.o    = $(shell if $(CC) $(CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi)
 
 EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/kernel/i8253.c b/arch/mips/kernel/i8253.c
new file mode 100644 (file)
index 0000000..475df69
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2006 IBM Corporation
+ *
+ * Implements device information for i8253 timer chip
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation
+ */
+
+#include <linux/platform_device.h>
+
+static __init int add_pcspkr(void)
+{
+       struct platform_device *pd;
+       int ret;
+
+       pd = platform_device_alloc("pcspkr", -1);
+       if (!pd)
+               return -ENOMEM;
+
+       ret = platform_device_add(pd);
+       if (ret)
+               platform_device_put(pd);
+
+       return ret;
+}
+device_initcall(add_pcspkr);
index 1b73508..2cbde86 100644 (file)
@@ -37,7 +37,7 @@
 #include <asm/prom.h>
 #include <asm/vdso_datapage.h>
 
-#define MODULE_VERS "1.6"
+#define MODULE_VERS "1.7"
 #define MODULE_NAME "lparcfg"
 
 /* #define LPARCFG_DEBUG */
@@ -149,17 +149,17 @@ static void log_plpar_hcall_return(unsigned long rc, char *tag)
        if (rc == 0)            /* success, return */
                return;
 /* check for null tag ? */
-       if (rc == H_Hardware)
+       if (rc == H_HARDWARE)
                printk(KERN_INFO
                       "plpar-hcall (%s) failed with hardware fault\n", tag);
-       else if (rc == H_Function)
+       else if (rc == H_FUNCTION)
                printk(KERN_INFO
                       "plpar-hcall (%s) failed; function not allowed\n", tag);
-       else if (rc == H_Authority)
+       else if (rc == H_AUTHORITY)
                printk(KERN_INFO
-                      "plpar-hcall (%s) failed; not authorized to this function\n",
-                      tag);
-       else if (rc == H_Parameter)
+                      "plpar-hcall (%s) failed; not authorized to this"
+                      " function\n", tag);
+       else if (rc == H_PARAMETER)
                printk(KERN_INFO "plpar-hcall (%s) failed; Bad parameter(s)\n",
                       tag);
        else
@@ -209,7 +209,7 @@ static void h_pic(unsigned long *pool_idle_time, unsigned long *num_procs)
        unsigned long dummy;
        rc = plpar_hcall(H_PIC, 0, 0, 0, 0, pool_idle_time, num_procs, &dummy);
 
-       if (rc != H_Authority)
+       if (rc != H_AUTHORITY)
                log_plpar_hcall_return(rc, "H_PIC");
 }
 
@@ -242,7 +242,7 @@ static void parse_system_parameter_string(struct seq_file *m)
 {
        int call_status;
 
-       char *local_buffer = kmalloc(SPLPAR_MAXLENGTH, GFP_KERNEL);
+       unsigned char *local_buffer = kmalloc(SPLPAR_MAXLENGTH, GFP_KERNEL);
        if (!local_buffer) {
                printk(KERN_ERR "%s %s kmalloc failure at line %d \n",
                       __FILE__, __FUNCTION__, __LINE__);
@@ -254,7 +254,8 @@ static void parse_system_parameter_string(struct seq_file *m)
        call_status = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1,
                                NULL,
                                SPLPAR_CHARACTERISTICS_TOKEN,
-                               __pa(rtas_data_buf));
+                               __pa(rtas_data_buf),
+                               RTAS_DATA_BUF_SIZE);
        memcpy(local_buffer, rtas_data_buf, SPLPAR_MAXLENGTH);
        spin_unlock(&rtas_data_buf_lock);
 
@@ -275,7 +276,7 @@ static void parse_system_parameter_string(struct seq_file *m)
 #ifdef LPARCFG_DEBUG
                printk(KERN_INFO "success calling get-system-parameter \n");
 #endif
-               splpar_strlen = local_buffer[0] * 16 + local_buffer[1];
+               splpar_strlen = local_buffer[0] * 256 + local_buffer[1];
                local_buffer += 2;      /* step over strlen value */
 
                memset(workbuffer, 0, SPLPAR_MAXLENGTH);
@@ -529,13 +530,13 @@ static ssize_t lparcfg_write(struct file *file, const char __user * buf,
        retval = plpar_hcall_norets(H_SET_PPP, *new_entitled_ptr,
                                    *new_weight_ptr);
 
-       if (retval == H_Success || retval == H_Constrained) {
+       if (retval == H_SUCCESS || retval == H_CONSTRAINED) {
                retval = count;
-       } else if (retval == H_Busy) {
+       } else if (retval == H_BUSY) {
                retval = -EBUSY;
-       } else if (retval == H_Hardware) {
+       } else if (retval == H_HARDWARE) {
                retval = -EIO;
-       } else if (retval == H_Parameter) {
+       } else if (retval == H_PARAMETER) {
                retval = -EINVAL;
        } else {
                printk(KERN_WARNING "%s: received unknown hv return code %ld",
index 06636c9..0112318 100644 (file)
@@ -578,18 +578,18 @@ static void rtas_percpu_suspend_me(void *info)
         * We use "waiting" to indicate our state.  As long
         * as it is >0, we are still trying to all join up.
         * If it goes to 0, we have successfully joined up and
-        * one thread got H_Continue.  If any error happens,
+        * one thread got H_CONTINUE.  If any error happens,
         * we set it to <0.
         */
        local_irq_save(flags);
        do {
                rc = plpar_hcall_norets(H_JOIN);
                smp_rmb();
-       } while (rc == H_Success && data->waiting > 0);
-       if (rc == H_Success)
+       } while (rc == H_SUCCESS && data->waiting > 0);
+       if (rc == H_SUCCESS)
                goto out;
 
-       if (rc == H_Continue) {
+       if (rc == H_CONTINUE) {
                data->waiting = 0;
                data->args->args[data->args->nargs] =
                        rtas_call(ibm_suspend_me_token, 0, 1, NULL);
@@ -597,7 +597,7 @@ static void rtas_percpu_suspend_me(void *info)
                        plpar_hcall_norets(H_PROD,i);
        } else {
                data->waiting = -EBUSY;
-               printk(KERN_ERR "Error on H_Join hypervisor call\n");
+               printk(KERN_ERR "Error on H_JOIN hypervisor call\n");
        }
 
 out:
@@ -624,7 +624,7 @@ static int rtas_ibm_suspend_me(struct rtas_args *args)
                printk(KERN_ERR "Error doing global join\n");
 
        /* Prod each CPU.  This won't hurt, and will wake
-        * anyone we successfully put to sleep with H_Join
+        * anyone we successfully put to sleep with H_JOIN.
         */
        for_each_possible_cpu(i)
                plpar_hcall_norets(H_PROD, i);
index c607f3b..1d93e73 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/reboot.h>
 #include <linux/delay.h>
 #include <linux/initrd.h>
+#include <linux/platform_device.h>
 #include <linux/ide.h>
 #include <linux/seq_file.h>
 #include <linux/ioport.h>
@@ -462,6 +463,29 @@ static int __init early_xmon(char *p)
 early_param("xmon", early_xmon);
 #endif
 
+static __init int add_pcspkr(void)
+{
+       struct device_node *np;
+       struct platform_device *pd;
+       int ret;
+
+       np = of_find_compatible_node(NULL, NULL, "pnpPNP,100");
+       of_node_put(np);
+       if (!np)
+               return -ENODEV;
+
+       pd = platform_device_alloc("pcspkr", -1);
+       if (!pd)
+               return -ENOMEM;
+
+       ret = platform_device_add(pd);
+       if (ret)
+               platform_device_put(pd);
+
+       return ret;
+}
+device_initcall(add_pcspkr);
+
 void probe_machine(void)
 {
        extern struct machdep_calls __machine_desc_start;
index a72bf5d..69ac257 100644 (file)
@@ -50,7 +50,6 @@
 #include <asm/kgdb.h>
 #endif
 
-extern void platform_init(void);
 extern void bootx_init(unsigned long r4, unsigned long phys);
 
 boot_infos_t *boot_infos;
@@ -138,12 +137,7 @@ void __init machine_init(unsigned long dt_ptr, unsigned long phys)
                strlcpy(cmd_line, CONFIG_CMDLINE, sizeof(cmd_line));
 #endif /* CONFIG_CMDLINE */
 
-#ifdef CONFIG_PPC_MULTIPLATFORM
        probe_machine();
-#else
-       /* Base init based on machine type. Obsoloete, please kill ! */
-       platform_init();
-#endif
 
 #ifdef CONFIG_6xx
        if (cpu_has_feature(CPU_FTR_CAN_DOZE) ||
index 59aa92c..13e91c4 100644 (file)
@@ -215,12 +215,10 @@ void __init early_setup(unsigned long dt_ptr)
        /*
         * Initialize stab / SLB management except on iSeries
         */
-       if (!firmware_has_feature(FW_FEATURE_ISERIES)) {
-               if (cpu_has_feature(CPU_FTR_SLB))
-                       slb_initialize();
-               else
-                       stab_initialize(get_paca()->stab_real);
-       }
+       if (cpu_has_feature(CPU_FTR_SLB))
+               slb_initialize();
+       else if (!firmware_has_feature(FW_FEATURE_ISERIES))
+               stab_initialize(get_paca()->stab_real);
 
        DBG(" <- early_setup()\n");
 }
index 1ad55f0..1424eab 100644 (file)
@@ -322,3 +322,4 @@ SYSCALL(spu_create)
 COMPAT_SYS(pselect6)
 COMPAT_SYS(ppoll)
 SYSCALL(unshare)
+SYSCALL(splice)
index 4cbde21..064a525 100644 (file)
@@ -228,7 +228,7 @@ void system_reset_exception(struct pt_regs *regs)
  */
 static inline int check_io_access(struct pt_regs *regs)
 {
-#ifdef CONFIG_PPC_PMAC
+#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32)
        unsigned long msr = regs->msr;
        const struct exception_table_entry *entry;
        unsigned int *nip = (unsigned int *)regs->nip;
@@ -261,7 +261,7 @@ static inline int check_io_access(struct pt_regs *regs)
                        return 1;
                }
        }
-#endif /* CONFIG_PPC_PMAC */
+#endif /* CONFIG_PPC_PMAC && CONFIG_PPC32 */
        return 0;
 }
 
@@ -308,8 +308,8 @@ platform_machine_check(struct pt_regs *regs)
 
 void machine_check_exception(struct pt_regs *regs)
 {
-#ifdef CONFIG_PPC64
        int recover = 0;
+       unsigned long reason = get_mc_reason(regs);
 
        /* See if any machine dependent calls */
        if (ppc_md.machine_check_exception)
@@ -317,8 +317,6 @@ void machine_check_exception(struct pt_regs *regs)
 
        if (recover)
                return;
-#else
-       unsigned long reason = get_mc_reason(regs);
 
        if (user_mode(regs)) {
                regs->msr |= MSR_RI;
@@ -462,7 +460,6 @@ void machine_check_exception(struct pt_regs *regs)
         * additional info, e.g. bus error registers.
         */
        platform_machine_check(regs);
-#endif /* CONFIG_PPC64 */
 
        if (debugger_fault_handler(regs))
                return;
index 5aea090..fdbba42 100644 (file)
@@ -177,15 +177,15 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
 
        /* When running in the kernel we expect faults to occur only to
         * addresses in user space.  All other faults represent errors in the
-        * kernel and should generate an OOPS.  Unfortunatly, in the case of an
-        * erroneous fault occuring in a code path which already holds mmap_sem
+        * kernel and should generate an OOPS.  Unfortunately, in the case of an
+        * erroneous fault occurring in a code path which already holds mmap_sem
         * we will deadlock attempting to validate the fault against the
         * address space.  Luckily the kernel only validly references user
         * space from well defined areas of code, which are listed in the
         * exceptions table.
         *
         * As the vast majority of faults will be valid we will only perform
-        * the source reference check when there is a possibilty of a deadlock.
+        * the source reference check when there is a possibility of a deadlock.
         * Attempt to lock the address space, if we cannot we then validate the
         * source.  If this is invalid we can skip the address space check,
         * thus avoiding the deadlock.
index 7c18b4c..7e789d2 100644 (file)
@@ -158,25 +158,25 @@ static int __init mpc834x_rtc_hookup(void)
 late_initcall(mpc834x_rtc_hookup);
 #endif
 
-void __init platform_init(void)
+/*
+ * Called very early, MMU is off, device-tree isn't unflattened
+ */
+static int __init mpc834x_sys_probe(void)
 {
-       /* setup the PowerPC module struct */
-       ppc_md.setup_arch = mpc834x_sys_setup_arch;
-
-       ppc_md.init_IRQ = mpc834x_sys_init_IRQ;
-       ppc_md.get_irq = ipic_get_irq;
-
-       ppc_md.restart = mpc83xx_restart;
-
-       ppc_md.time_init = mpc83xx_time_init;
-       ppc_md.set_rtc_time = NULL;
-       ppc_md.get_rtc_time = NULL;
-       ppc_md.calibrate_decr = generic_calibrate_decr;
-
-       ppc_md.progress = udbg_progress;
-
-       if (ppc_md.progress)
-               ppc_md.progress("mpc834x_sys_init(): exit", 0);
-
-       return;
+       /* We always match for now, eventually we should look at the flat
+          dev tree to ensure this is the board we are suppose to run on
+       */
+       return 1;
 }
+
+define_machine(mpc834x_sys) {
+       .name                   = "MPC834x SYS",
+       .probe                  = mpc834x_sys_probe,
+       .setup_arch             = mpc834x_sys_setup_arch,
+       .init_IRQ               = mpc834x_sys_init_IRQ,
+       .get_irq                = ipic_get_irq,
+       .restart                = mpc83xx_restart,
+       .time_init              = mpc83xx_time_init,
+       .calibrate_decr         = generic_calibrate_decr,
+       .progress               = udbg_progress,
+};
index b7821db..5eeff37 100644 (file)
@@ -220,25 +220,25 @@ void mpc85xx_ads_show_cpuinfo(struct seq_file *m)
        seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024));
 }
 
-void __init platform_init(void)
+/*
+ * Called very early, device-tree isn't unflattened
+ */
+static int __init mpc85xx_ads_probe(void)
 {
-       ppc_md.setup_arch = mpc85xx_ads_setup_arch;
-       ppc_md.show_cpuinfo = mpc85xx_ads_show_cpuinfo;
-
-       ppc_md.init_IRQ = mpc85xx_ads_pic_init;
-       ppc_md.get_irq = mpic_get_irq;
-
-       ppc_md.restart = mpc85xx_restart;
-       ppc_md.power_off = NULL;
-       ppc_md.halt = NULL;
-
-       ppc_md.time_init = NULL;
-       ppc_md.set_rtc_time = NULL;
-       ppc_md.get_rtc_time = NULL;
-       ppc_md.calibrate_decr = generic_calibrate_decr;
-
-       ppc_md.progress = udbg_progress;
-
-       if (ppc_md.progress)
-               ppc_md.progress("mpc85xx_ads platform_init(): exit", 0);
+       /* We always match for now, eventually we should look at the flat
+          dev tree to ensure this is the board we are suppose to run on
+       */
+       return 1;
 }
+
+define_machine(mpc85xx_ads) {
+       .name                   = "MPC85xx ADS",
+       .probe                  = mpc85xx_ads_probe,
+       .setup_arch             = mpc85xx_ads_setup_arch,
+       .init_IRQ               = mpc85xx_ads_pic_init,
+       .show_cpuinfo           = mpc85xx_ads_show_cpuinfo,
+       .get_irq                = mpic_get_irq,
+       .restart                = mpc85xx_restart,
+       .calibrate_decr         = generic_calibrate_decr,
+       .progress               = udbg_progress,
+};
index 3a4245c..6594bec 100644 (file)
@@ -316,6 +316,7 @@ void *spu_syscall_table[] = {
        [__NR_pselect6]                 sys_ni_syscall, /* sys_pselect */
        [__NR_ppoll]                    sys_ni_syscall, /* sys_ppoll */
        [__NR_unshare]                  sys_unshare,
+       [__NR_splice]                   sys_splice,
 };
 
 long spu_sys_callback(struct spu_syscall_block *s)
index c04e078..483c8b7 100644 (file)
@@ -2,6 +2,7 @@
 #include <linux/ptrace.h>
 
 #include <asm/spu.h>
+#include <asm/unistd.h>
 
 #include "spufs.h"
 
index 9b2b1cb..780fb27 100644 (file)
@@ -865,7 +865,7 @@ void __init eeh_init(void)
  * on the CEC architecture, type of the device, on earlier boot
  * command-line arguments & etc.
  */
-void eeh_add_device_early(struct device_node *dn)
+static void eeh_add_device_early(struct device_node *dn)
 {
        struct pci_controller *phb;
        struct eeh_early_enable_info info;
@@ -882,7 +882,6 @@ void eeh_add_device_early(struct device_node *dn)
        info.buid_lo = BUID_LO(phb->buid);
        early_enable_eeh(dn, &info);
 }
-EXPORT_SYMBOL_GPL(eeh_add_device_early);
 
 void eeh_add_device_tree_early(struct device_node *dn)
 {
@@ -893,20 +892,6 @@ void eeh_add_device_tree_early(struct device_node *dn)
 }
 EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
 
-void eeh_add_device_tree_late(struct pci_bus *bus)
-{
-       struct pci_dev *dev;
-
-       list_for_each_entry(dev, &bus->devices, bus_list) {
-               eeh_add_device_late(dev);
-               if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
-                       struct pci_bus *subbus = dev->subordinate;
-                       if (subbus)
-                               eeh_add_device_tree_late(subbus);
-               }
-       }
-}
-
 /**
  * eeh_add_device_late - perform EEH initialization for the indicated pci device
  * @dev: pci device for which to set up EEH
@@ -914,7 +899,7 @@ void eeh_add_device_tree_late(struct pci_bus *bus)
  * This routine must be used to complete EEH initialization for PCI
  * devices that were added after system boot (e.g. hotplug, dlpar).
  */
-void eeh_add_device_late(struct pci_dev *dev)
+static void eeh_add_device_late(struct pci_dev *dev)
 {
        struct device_node *dn;
        struct pci_dn *pdn;
@@ -933,16 +918,33 @@ void eeh_add_device_late(struct pci_dev *dev)
 
        pci_addr_cache_insert_device (dev);
 }
-EXPORT_SYMBOL_GPL(eeh_add_device_late);
+
+void eeh_add_device_tree_late(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               eeh_add_device_late(dev);
+               if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+                       struct pci_bus *subbus = dev->subordinate;
+                       if (subbus)
+                               eeh_add_device_tree_late(subbus);
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
 
 /**
  * eeh_remove_device - undo EEH setup for the indicated pci device
  * @dev: pci device to be removed
  *
- * This routine should be when a device is removed from a running
- * system (e.g. by hotplug or dlpar).
+ * This routine should be called when a device is removed from
+ * a running system (e.g. by hotplug or dlpar).  It unregisters
+ * the PCI device from the EEH subsystem.  I/O errors affecting
+ * this device will no longer be detected after this call; thus,
+ * i/o errors affecting this slot may leave this device unusable.
  */
-void eeh_remove_device(struct pci_dev *dev)
+static void eeh_remove_device(struct pci_dev *dev)
 {
        struct device_node *dn;
        if (!dev || !eeh_subsystem_enabled)
@@ -958,21 +960,17 @@ void eeh_remove_device(struct pci_dev *dev)
        PCI_DN(dn)->pcidev = NULL;
        pci_dev_put (dev);
 }
-EXPORT_SYMBOL_GPL(eeh_remove_device);
 
 void eeh_remove_bus_device(struct pci_dev *dev)
 {
+       struct pci_bus *bus = dev->subordinate;
+       struct pci_dev *child, *tmp;
+
        eeh_remove_device(dev);
-       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
-               struct pci_bus *bus = dev->subordinate;
-               struct list_head *ln;
-               if (!bus)
-                       return; 
-               for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) {
-                       struct pci_dev *pdev = pci_dev_b(ln);
-                       if (pdev)
-                               eeh_remove_bus_device(pdev);
-               }
+
+       if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+               list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
+                        eeh_remove_bus_device(child);
        }
 }
 EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
index cc2495a..1fba695 100644 (file)
@@ -293,15 +293,16 @@ void handle_eeh_events (struct eeh_event *event)
        frozen_pdn = PCI_DN(frozen_dn);
        frozen_pdn->eeh_freeze_count++;
 
-       pci_str = pci_name (frozen_pdn->pcidev);
-       drv_str = pcid_name (frozen_pdn->pcidev);
-       if (!pci_str) {
+       if (frozen_pdn->pcidev) {
+               pci_str = pci_name (frozen_pdn->pcidev);
+               drv_str = pcid_name (frozen_pdn->pcidev);
+       } else {
                pci_str = pci_name (event->dev);
                drv_str = pcid_name (event->dev);
        }
        
        if (frozen_pdn->eeh_freeze_count > EEH_MAX_ALLOWED_FREEZES)
-               goto hard_fail;
+               goto excess_failures;
 
        /* If the reset state is a '5' and the time to reset is 0 (infinity)
         * or is more then 15 seconds, then mark this as a permanent failure.
@@ -356,7 +357,7 @@ void handle_eeh_events (struct eeh_event *event)
 
        return;
        
-hard_fail:
+excess_failures:
        /*
         * About 90% of all real-life EEH failures in the field
         * are due to poorly seated PCI cards. Only 10% or so are
@@ -367,7 +368,15 @@ hard_fail:
           "and has been permanently disabled.  Please try reseating\n"
           "this device or replacing it.\n",
                drv_str, pci_str, frozen_pdn->eeh_freeze_count);
+       goto perm_error;
+
+hard_fail:
+       printk(KERN_ERR
+          "EEH: Unable to recover from failure of PCI device %s - %s\n"
+          "Please try reseating this device or replacing it.\n",
+               drv_str, pci_str);
 
+perm_error:
        eeh_slot_error_detail(frozen_pdn, 2 /* Permanent Error */);
 
        /* Notify all devices that they're about to go down. */
index 9a9961f..a1bda6f 100644 (file)
@@ -19,7 +19,9 @@
  */
 
 #include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/pci.h>
+#include <linux/workqueue.h>
 #include <asm/eeh_event.h>
 #include <asm/ppc-pci.h>
 
@@ -37,14 +39,18 @@ LIST_HEAD(eeh_eventlist);
 static void eeh_thread_launcher(void *);
 DECLARE_WORK(eeh_event_wq, eeh_thread_launcher, NULL);
 
+/* Serialize reset sequences for a given pci device */
+DEFINE_MUTEX(eeh_event_mutex);
+
 /**
- * eeh_event_handler - dispatch EEH events.  The detection of a frozen
- * slot can occur inside an interrupt, where it can be hard to do
- * anything about it.  The goal of this routine is to pull these
- * detection events out of the context of the interrupt handler, and
- * re-dispatch them for processing at a later time in a normal context.
- *
+ * eeh_event_handler - dispatch EEH events.
  * @dummy - unused
+ *
+ * The detection of a frozen slot can occur inside an interrupt,
+ * where it can be hard to do anything about it.  The goal of this
+ * routine is to pull these detection events out of the context
+ * of the interrupt handler, and re-dispatch them for processing
+ * at a later time in a normal context.
  */
 static int eeh_event_handler(void * dummy)
 {
@@ -64,23 +70,24 @@ static int eeh_event_handler(void * dummy)
                        event = list_entry(eeh_eventlist.next, struct eeh_event, list);
                        list_del(&event->list);
                }
-               
-               if (event)
-                       eeh_mark_slot(event->dn, EEH_MODE_RECOVERING);
-
                spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
+
                if (event == NULL)
                        break;
 
+               /* Serialize processing of EEH events */
+               mutex_lock(&eeh_event_mutex);
+               eeh_mark_slot(event->dn, EEH_MODE_RECOVERING);
+
                printk(KERN_INFO "EEH: Detected PCI bus error on device %s\n",
                       pci_name(event->dev));
 
                handle_eeh_events(event);
 
                eeh_clear_slot(event->dn, EEH_MODE_RECOVERING);
-
                pci_dev_put(event->dev);
                kfree(event);
+               mutex_unlock(&eeh_event_mutex);
        }
 
        return 0;
@@ -88,7 +95,6 @@ static int eeh_event_handler(void * dummy)
 
 /**
  * eeh_thread_launcher
- *
  * @dummy - unused
  */
 static void eeh_thread_launcher(void *dummy)
index db7c19f..c9ff547 100644 (file)
@@ -127,3 +127,103 @@ _GLOBAL(plpar_hcall_4out)
 
        mtcrf   0xff,r0
        blr                             /* return r3 = status */
+
+/* plpar_hcall_7arg_7ret(unsigned long opcode,         R3
+                        unsigned long arg1,            R4
+                        unsigned long arg2,            R5
+                        unsigned long arg3,            R6
+                        unsigned long arg4,            R7
+                        unsigned long arg5,            R8
+                        unsigned long arg6,            R9
+                        unsigned long arg7,            R10
+                        unsigned long *out1,           112(R1)
+                        unsigned long *out2,           110(R1)
+                        unsigned long *out3,           108(R1)
+                        unsigned long *out4,           106(R1)
+                        unsigned long *out5,           104(R1)
+                        unsigned long *out6,           102(R1)
+                        unsigned long *out7);          100(R1)
+*/
+_GLOBAL(plpar_hcall_7arg_7ret)
+       HMT_MEDIUM
+
+       mfcr    r0
+       stw     r0,8(r1)
+
+       HVSC                            /* invoke the hypervisor */
+
+       lwz     r0,8(r1)
+
+       ld      r11,STK_PARM(r11)(r1)   /* Fetch r4 ret arg */
+       std     r4,0(r11)
+       ld      r11,STK_PARM(r12)(r1)   /* Fetch r5 ret arg */
+       std     r5,0(r11)
+       ld      r11,STK_PARM(r13)(r1)   /* Fetch r6 ret arg */
+       std     r6,0(r11)
+       ld      r11,STK_PARM(r14)(r1)   /* Fetch r7 ret arg */
+       std     r7,0(r11)
+       ld      r11,STK_PARM(r15)(r1)   /* Fetch r8 ret arg */
+       std     r8,0(r11)
+       ld      r11,STK_PARM(r16)(r1)   /* Fetch r9 ret arg */
+       std     r9,0(r11)
+       ld      r11,STK_PARM(r17)(r1)   /* Fetch r10 ret arg */
+       std     r10,0(r11)
+
+       mtcrf   0xff,r0
+
+       blr                             /* return r3 = status */
+
+/* plpar_hcall_9arg_9ret(unsigned long opcode,         R3
+                        unsigned long arg1,            R4
+                        unsigned long arg2,            R5
+                        unsigned long arg3,            R6
+                        unsigned long arg4,            R7
+                        unsigned long arg5,            R8
+                        unsigned long arg6,            R9
+                        unsigned long arg7,            R10
+                        unsigned long arg8,            112(R1)
+                        unsigned long arg9,            110(R1)
+                        unsigned long *out1,           108(R1)
+                        unsigned long *out2,           106(R1)
+                        unsigned long *out3,           104(R1)
+                        unsigned long *out4,           102(R1)
+                        unsigned long *out5,           100(R1)
+                        unsigned long *out6,            98(R1)
+                        unsigned long *out7);           96(R1)
+                        unsigned long *out8,            94(R1)
+                        unsigned long *out9,            92(R1)
+*/
+_GLOBAL(plpar_hcall_9arg_9ret)
+       HMT_MEDIUM
+
+       mfcr    r0
+       stw     r0,8(r1)
+
+       ld      r11,STK_PARM(r11)(r1)    /* put arg8 in R11 */
+       ld      r12,STK_PARM(r12)(r1)    /* put arg9 in R12 */
+
+       HVSC                            /* invoke the hypervisor */
+
+       ld      r0,STK_PARM(r13)(r1)    /* Fetch r4 ret arg */
+       stdx    r4,r0,r0
+       ld      r0,STK_PARM(r14)(r1)    /* Fetch r5 ret arg */
+       stdx    r5,r0,r0
+       ld      r0,STK_PARM(r15)(r1)    /* Fetch r6 ret arg */
+       stdx    r6,r0,r0
+       ld      r0,STK_PARM(r16)(r1)    /* Fetch r7 ret arg */
+       stdx    r7,r0,r0
+       ld      r0,STK_PARM(r17)(r1)    /* Fetch r8 ret arg */
+       stdx    r8,r0,r0
+       ld      r0,STK_PARM(r18)(r1)    /* Fetch r9 ret arg */
+       stdx    r9,r0,r0
+       ld      r0,STK_PARM(r19)(r1)    /* Fetch r10 ret arg */
+       stdx    r10,r0,r0
+       ld      r0,STK_PARM(r20)(r1)    /* Fetch r11 ret arg */
+       stdx    r11,r0,r0
+       ld      r0,STK_PARM(r21)(r1)    /* Fetch r12 ret arg */
+       stdx    r12,r0,r0
+
+       lwz     r0,8(r1)
+       mtcrf   0xff,r0
+
+       blr                             /* return r3 = status */
index ba6befd..a72a987 100644 (file)
@@ -41,7 +41,7 @@ int hvc_get_chars(uint32_t vtermno, char *buf, int count)
        unsigned long got;
 
        if (plpar_hcall(H_GET_TERM_CHAR, vtermno, 0, 0, 0, &got,
-               (unsigned long *)buf, (unsigned long *)buf+1) == H_Success)
+               (unsigned long *)buf, (unsigned long *)buf+1) == H_SUCCESS)
                return got;
        return 0;
 }
@@ -69,9 +69,9 @@ int hvc_put_chars(uint32_t vtermno, const char *buf, int count)
 
        ret = plpar_hcall_norets(H_PUT_TERM_CHAR, vtermno, count, lbuf[0],
                                 lbuf[1]);
-       if (ret == H_Success)
+       if (ret == H_SUCCESS)
                return count;
-       if (ret == H_Busy)
+       if (ret == H_BUSY)
                return 0;
        return -EIO;
 }
index 22bfb5c..fcf4b4c 100644 (file)
@@ -43,21 +43,21 @@ MODULE_VERSION(HVCS_ARCH_VERSION);
 static int hvcs_convert(long to_convert)
 {
        switch (to_convert) {
-               case H_Success:
+               case H_SUCCESS:
                        return 0;
-               case H_Parameter:
+               case H_PARAMETER:
                        return -EINVAL;
-               case H_Hardware:
+               case H_HARDWARE:
                        return -EIO;
-               case H_Busy:
-               case H_LongBusyOrder1msec:
-               case H_LongBusyOrder10msec:
-               case H_LongBusyOrder100msec:
-               case H_LongBusyOrder1sec:
-               case H_LongBusyOrder10sec:
-               case H_LongBusyOrder100sec:
+               case H_BUSY:
+               case H_LONG_BUSY_ORDER_1_MSEC:
+               case H_LONG_BUSY_ORDER_10_MSEC:
+               case H_LONG_BUSY_ORDER_100_MSEC:
+               case H_LONG_BUSY_ORDER_1_SEC:
+               case H_LONG_BUSY_ORDER_10_SEC:
+               case H_LONG_BUSY_ORDER_100_SEC:
                        return -EBUSY;
-               case H_Function: /* fall through */
+               case H_FUNCTION: /* fall through */
                default:
                        return -EPERM;
        }
index 8952528..634b7d0 100644 (file)
@@ -54,7 +54,8 @@ EXPORT_SYMBOL(plpar_hcall);
 EXPORT_SYMBOL(plpar_hcall_4out);
 EXPORT_SYMBOL(plpar_hcall_norets);
 EXPORT_SYMBOL(plpar_hcall_8arg_2ret);
-
+EXPORT_SYMBOL(plpar_hcall_7arg_7ret);
+EXPORT_SYMBOL(plpar_hcall_9arg_9ret);
 extern void pSeries_find_serial_port(void);
 
 
@@ -72,7 +73,7 @@ static void udbg_hvsi_putc(char c)
 
        do {
                rc = plpar_put_term_char(vtermno, sizeof(packet), packet);
-       } while (rc == H_Busy);
+       } while (rc == H_BUSY);
 }
 
 static long hvsi_udbg_buf_len;
@@ -85,7 +86,7 @@ static int udbg_hvsi_getc_poll(void)
 
        if (hvsi_udbg_buf_len == 0) {
                rc = plpar_get_term_char(vtermno, &hvsi_udbg_buf_len, hvsi_udbg_buf);
-               if (rc != H_Success || hvsi_udbg_buf[0] != 0xff) {
+               if (rc != H_SUCCESS || hvsi_udbg_buf[0] != 0xff) {
                        /* bad read or non-data packet */
                        hvsi_udbg_buf_len = 0;
                } else {
@@ -139,7 +140,7 @@ static void udbg_putcLP(char c)
        buf[0] = c;
        do {
                rc = plpar_put_term_char(vtermno, 1, buf);
-       } while(rc == H_Busy);
+       } while(rc == H_BUSY);
 }
 
 /* Buffered chars getc */
@@ -158,7 +159,7 @@ static int udbg_getc_pollLP(void)
                /* get some more chars. */
                inbuflen = 0;
                rc = plpar_get_term_char(vtermno, &inbuflen, buf);
-               if (rc != H_Success)
+               if (rc != H_SUCCESS)
                        inbuflen = 0;   /* otherwise inbuflen is garbage */
        }
        if (inbuflen <= 0 || inbuflen > 16) {
@@ -304,7 +305,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group,
 
        lpar_rc = plpar_hcall(H_ENTER, flags, hpte_group, hpte_v,
                              hpte_r, &slot, &dummy0, &dummy1);
-       if (unlikely(lpar_rc == H_PTEG_Full)) {
+       if (unlikely(lpar_rc == H_PTEG_FULL)) {
                if (!(vflags & HPTE_V_BOLTED))
                        DBG_LOW(" full\n");
                return -1;
@@ -315,7 +316,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group,
         * will fail. However we must catch the failure in hash_page
         * or we will loop forever, so return -2 in this case.
         */
-       if (unlikely(lpar_rc != H_Success)) {
+       if (unlikely(lpar_rc != H_SUCCESS)) {
                if (!(vflags & HPTE_V_BOLTED))
                        DBG_LOW(" lpar err %d\n", lpar_rc);
                return -2;
@@ -346,9 +347,9 @@ static long pSeries_lpar_hpte_remove(unsigned long hpte_group)
                /* don't remove a bolted entry */
                lpar_rc = plpar_pte_remove(H_ANDCOND, hpte_group + slot_offset,
                                           (0x1UL << 4), &dummy1, &dummy2);
-               if (lpar_rc == H_Success)
+               if (lpar_rc == H_SUCCESS)
                        return i;
-               BUG_ON(lpar_rc != H_Not_Found);
+               BUG_ON(lpar_rc != H_NOT_FOUND);
 
                slot_offset++;
                slot_offset &= 0x7;
@@ -391,14 +392,14 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot,
 
        lpar_rc = plpar_pte_protect(flags, slot, want_v & HPTE_V_AVPN);
 
-       if (lpar_rc == H_Not_Found) {
+       if (lpar_rc == H_NOT_FOUND) {
                DBG_LOW("not found !\n");
                return -1;
        }
 
        DBG_LOW("ok\n");
 
-       BUG_ON(lpar_rc != H_Success);
+       BUG_ON(lpar_rc != H_SUCCESS);
 
        return 0;
 }
@@ -417,7 +418,7 @@ static unsigned long pSeries_lpar_hpte_getword0(unsigned long slot)
 
        lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1);
 
-       BUG_ON(lpar_rc != H_Success);
+       BUG_ON(lpar_rc != H_SUCCESS);
 
        return dword0;
 }
@@ -468,7 +469,7 @@ static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp,
        flags = newpp & 7;
        lpar_rc = plpar_pte_protect(flags, slot, 0);
 
-       BUG_ON(lpar_rc != H_Success);
+       BUG_ON(lpar_rc != H_SUCCESS);
 }
 
 static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
@@ -484,10 +485,10 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
        want_v = hpte_encode_v(va, psize);
        lpar_rc = plpar_pte_remove(H_AVPN, slot, want_v & HPTE_V_AVPN,
                                   &dummy1, &dummy2);
-       if (lpar_rc == H_Not_Found)
+       if (lpar_rc == H_NOT_FOUND)
                return;
 
-       BUG_ON(lpar_rc != H_Success);
+       BUG_ON(lpar_rc != H_SUCCESS);
 }
 
 /*
index b2fbf8b..5eb55ef 100644 (file)
@@ -463,7 +463,7 @@ static void pseries_dedicated_idle_sleep(void)
         * very low priority.  The cede enables interrupts, which
         * doesn't matter here.
         */
-       if (!lppaca[cpu ^ 1].idle || poll_pending() == H_Pending)
+       if (!lppaca[cpu ^ 1].idle || poll_pending() == H_PENDING)
                cede_processor();
 
 out:
index 866379b..8e53e04 100644 (file)
@@ -258,7 +258,7 @@ EXPORT_SYMBOL(vio_find_node);
 int vio_enable_interrupts(struct vio_dev *dev)
 {
        int rc = h_vio_signal(dev->unit_address, VIO_IRQ_ENABLE);
-       if (rc != H_Success)
+       if (rc != H_SUCCESS)
                printk(KERN_ERR "vio: Error 0x%x enabling interrupts\n", rc);
        return rc;
 }
@@ -267,7 +267,7 @@ EXPORT_SYMBOL(vio_enable_interrupts);
 int vio_disable_interrupts(struct vio_dev *dev)
 {
        int rc = h_vio_signal(dev->unit_address, VIO_IRQ_DISABLE);
-       if (rc != H_Success)
+       if (rc != H_SUCCESS)
                printk(KERN_ERR "vio: Error 0x%x disabling interrupts\n", rc);
        return rc;
 }
index 4864cb3..2d60ea3 100644 (file)
@@ -168,7 +168,7 @@ static int pSeriesLP_xirr_info_get(int n_cpu)
        unsigned long return_value;
 
        lpar_rc = plpar_xirr(&return_value);
-       if (lpar_rc != H_Success)
+       if (lpar_rc != H_SUCCESS)
                panic(" bad return code xirr - rc = %lx \n", lpar_rc);
        return (int)return_value;
 }
@@ -179,7 +179,7 @@ static void pSeriesLP_xirr_info_set(int n_cpu, int value)
        unsigned long val64 = value & 0xffffffff;
 
        lpar_rc = plpar_eoi(val64);
-       if (lpar_rc != H_Success)
+       if (lpar_rc != H_SUCCESS)
                panic("bad return code EOI - rc = %ld, value=%lx\n", lpar_rc,
                      val64);
 }
@@ -189,7 +189,7 @@ void pSeriesLP_cppr_info(int n_cpu, u8 value)
        unsigned long lpar_rc;
 
        lpar_rc = plpar_cppr(value);
-       if (lpar_rc != H_Success)
+       if (lpar_rc != H_SUCCESS)
                panic("bad return code cppr - rc = %lx\n", lpar_rc);
 }
 
@@ -198,7 +198,7 @@ static void pSeriesLP_qirr_info(int n_cpu , u8 value)
        unsigned long lpar_rc;
 
        lpar_rc = plpar_ipi(get_hard_smp_processor_id(n_cpu), value);
-       if (lpar_rc != H_Success)
+       if (lpar_rc != H_SUCCESS)
                panic("bad return code qirr - rc = %lx\n", lpar_rc);
 }
 
index 768de64..fbbec5e 100644 (file)
@@ -64,13 +64,13 @@ sys_call_table:
 /*215*/        .long sys_ipc, sys_sigreturn, sys_clone, sys_ioprio_get, sys_adjtimex
 /*220*/        .long sys_sigprocmask, sys_ni_syscall, sys_delete_module, sys_ni_syscall, sys_getpgid
 /*225*/        .long sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid16, sys_setfsgid16
-/*230*/        .long sys_select, sys_time, sys_nis_syscall, sys_stime, sys_statfs64
+/*230*/        .long sys_select, sys_time, sys_splice, sys_stime, sys_statfs64
                                          /* "We are the Knights of the Forest of Ni!!" */
 /*235*/        .long sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
 /*240*/        .long sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
 /*245*/        .long sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
 /*250*/        .long sparc_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
-/*255*/        .long sys_nis_syscall, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep
+/*255*/        .long sys_sync_file_range, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep
 /*260*/        .long sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun
 /*265*/        .long sys_timer_delete, sys_timer_create, sys_nis_syscall, sys_io_setup, sys_io_destroy
 /*270*/        .long sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink
index 900fb0b..3038908 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
 # Linux kernel version: 2.6.16
-# Sun Mar 26 14:58:11 2006
+# Fri Mar 31 01:40:57 2006
 #
 CONFIG_SPARC=y
 CONFIG_SPARC64=y
@@ -180,6 +180,7 @@ CONFIG_SYN_COOKIES=y
 CONFIG_INET_AH=y
 CONFIG_INET_ESP=y
 CONFIG_INET_IPCOMP=y
+CONFIG_INET_XFRM_TUNNEL=y
 CONFIG_INET_TUNNEL=y
 CONFIG_INET_DIAG=y
 CONFIG_INET_TCP_DIAG=y
@@ -203,6 +204,7 @@ CONFIG_IPV6_ROUTE_INFO=y
 CONFIG_INET6_AH=m
 CONFIG_INET6_ESP=m
 CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_XFRM_TUNNEL=m
 CONFIG_INET6_TUNNEL=m
 CONFIG_IPV6_TUNNEL=m
 # CONFIG_NETFILTER is not set
@@ -308,7 +310,6 @@ CONFIG_BLK_DEV_NBD=m
 # CONFIG_BLK_DEV_SX8 is not set
 CONFIG_BLK_DEV_UB=m
 # CONFIG_BLK_DEV_RAM is not set
-CONFIG_BLK_DEV_RAM_COUNT=16
 # CONFIG_BLK_DEV_INITRD is not set
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_CDROM_PKTCDVD_BUFFERS=8
@@ -449,6 +450,7 @@ CONFIG_MD_RAID0=m
 CONFIG_MD_RAID1=m
 CONFIG_MD_RAID10=m
 CONFIG_MD_RAID5=m
+# CONFIG_MD_RAID5_RESHAPE is not set
 CONFIG_MD_RAID6=m
 CONFIG_MD_MULTIPATH=m
 # CONFIG_MD_FAULTY is not set
@@ -741,9 +743,7 @@ CONFIG_I2C_ALGOBIT=y
 # CONFIG_SENSORS_PCF8574 is not set
 # CONFIG_SENSORS_PCA9539 is not set
 # CONFIG_SENSORS_PCF8591 is not set
-# CONFIG_SENSORS_RTC8564 is not set
 # CONFIG_SENSORS_MAX6875 is not set
-# CONFIG_RTC_X1205_I2C is not set
 # CONFIG_I2C_DEBUG_CORE is not set
 # CONFIG_I2C_DEBUG_ALGO is not set
 # CONFIG_I2C_DEBUG_BUS is not set
@@ -826,6 +826,7 @@ CONFIG_FB_CFB_FILLRECT=y
 CONFIG_FB_CFB_COPYAREA=y
 CONFIG_FB_CFB_IMAGEBLIT=y
 # CONFIG_FB_MACMODES is not set
+# CONFIG_FB_FIRMWARE_EDID is not set
 CONFIG_FB_MODE_HELPERS=y
 CONFIG_FB_TILEBLITTING=y
 # CONFIG_FB_CIRRUS is not set
@@ -1116,6 +1117,11 @@ CONFIG_USB_HIDDEV=y
 # EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
 #
 
+#
+# Real Time Clock
+#
+# CONFIG_RTC_CLASS is not set
+
 #
 # Misc Linux/SPARC drivers
 #
index 7dc28a4..8175a69 100644 (file)
@@ -830,9 +830,16 @@ void smp_call_function_client(int irq, struct pt_regs *regs)
 
 static void tsb_sync(void *info)
 {
+       struct trap_per_cpu *tp = &trap_block[raw_smp_processor_id()];
        struct mm_struct *mm = info;
 
-       if (current->active_mm == mm)
+       /* It is not valid to test "currrent->active_mm == mm" here.
+        *
+        * The value of "current" is not changed atomically with
+        * switch_mm().  But that's OK, we just need to check the
+        * current cpu's trap block PGD physical address.
+        */
+       if (tp->pgd_paddr == __pa(mm->pgd))
                tsb_context_switch(mm);
 }
 
index c4a1cef..86dd5cb 100644 (file)
@@ -136,6 +136,8 @@ SIGN1(sys32_getpeername, sys_getpeername, %o0)
 SIGN1(sys32_getsockname, sys_getsockname, %o0)
 SIGN2(sys32_ioprio_get, sys_ioprio_get, %o0, %o1)
 SIGN3(sys32_ioprio_set, sys_ioprio_set, %o0, %o1, %o2)
+SIGN2(sys32_splice, sys_splice, %o0, %o1)
+SIGN2(sys32_sync_file_range, compat_sync_file_range, %o0, %o5)
 
        .globl          sys32_mmap2
 sys32_mmap2:
index 2e906ba..31030bf 100644 (file)
@@ -1069,3 +1069,11 @@ long sys32_lookup_dcookie(unsigned long cookie_high,
        return sys_lookup_dcookie((cookie_high << 32) | cookie_low,
                                  buf, len);
 }
+
+long compat_sync_file_range(int fd, unsigned long off_high, unsigned long off_low, unsigned long nb_high, unsigned long nb_low, int flags)
+{
+       return sys_sync_file_range(fd,
+                                  (off_high << 32) | off_low,
+                                  (nb_high << 32) | nb_low,
+                                  flags);
+}
index 3b250f2..857b82c 100644 (file)
@@ -66,12 +66,12 @@ sys_call_table32:
        .word sys32_ipc, sys32_sigreturn, sys_clone, sys32_ioprio_get, compat_sys_adjtimex
 /*220*/        .word sys32_sigprocmask, sys_ni_syscall, sys32_delete_module, sys_ni_syscall, sys32_getpgid
        .word sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys32_setfsuid16, sys32_setfsgid16
-/*230*/        .word sys32_select, compat_sys_time, sys_nis_syscall, compat_sys_stime, compat_sys_statfs64
+/*230*/        .word sys32_select, compat_sys_time, sys32_splice, compat_sys_stime, compat_sys_statfs64
        .word compat_sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys32_mlockall
 /*240*/        .word sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys32_sched_setscheduler, sys32_sched_getscheduler
        .word sys_sched_yield, sys32_sched_get_priority_max, sys32_sched_get_priority_min, sys32_sched_rr_get_interval, compat_sys_nanosleep
 /*250*/        .word sys32_mremap, sys32_sysctl, sys32_getsid, sys_fdatasync, sys32_nfsservctl
-       .word sys_ni_syscall, compat_sys_clock_settime, compat_sys_clock_gettime, compat_sys_clock_getres, sys32_clock_nanosleep
+       .word sys32_sync_file_range, compat_sys_clock_settime, compat_sys_clock_gettime, compat_sys_clock_getres, sys32_clock_nanosleep
 /*260*/        .word compat_sys_sched_getaffinity, compat_sys_sched_setaffinity, sys32_timer_settime, compat_sys_timer_gettime, sys_timer_getoverrun
        .word sys_timer_delete, compat_sys_timer_create, sys_ni_syscall, compat_sys_io_setup, sys_io_destroy
 /*270*/        .word sys32_io_submit, sys_io_cancel, compat_sys_io_getevents, sys32_mq_open, sys_mq_unlink
@@ -135,12 +135,12 @@ sys_call_table:
        .word sys_ipc, sys_nis_syscall, sys_clone, sys_ioprio_get, sys_adjtimex
 /*220*/        .word sys_nis_syscall, sys_ni_syscall, sys_delete_module, sys_ni_syscall, sys_getpgid
        .word sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid
-/*230*/        .word sys_select, sys_nis_syscall, sys_nis_syscall, sys_stime, sys_statfs64
+/*230*/        .word sys_select, sys_nis_syscall, sys_splice, sys_stime, sys_statfs64
        .word sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
 /*240*/        .word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
        .word sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
 /*250*/        .word sys64_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
-       .word sys_ni_syscall, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep
+       .word sys_sync_file_range, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep
 /*260*/        .word sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun
        .word sys_timer_delete, sys_timer_create, sys_ni_syscall, sys_io_setup, sys_io_destroy
 /*270*/        .word sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink
index 0db2f7d..6e002aa 100644 (file)
@@ -327,8 +327,12 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)
                insn = get_fault_insn(regs, 0);
                if (!insn)
                        goto continue_fault;
+               /* All loads, stores and atomics have bits 30 and 31 both set
+                * in the instruction.  Bit 21 is set in all stores, but we
+                * have to avoid prefetches which also have bit 21 set.
+                */
                if ((insn & 0xc0200000) == 0xc0200000 &&
-                   (insn & 0x1780000) != 0x1680000) {
+                   (insn & 0x01780000) != 0x01680000) {
                        /* Don't bother updating thread struct value,
                         * because update_mmu_cache only cares which tlb
                         * the access came from.
index 074620d..fbbbebb 100644 (file)
@@ -198,6 +198,13 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr)
        pmd_t *pmd;
        pte_t *pte = NULL;
 
+       /* We must align the address, because our caller will run
+        * set_huge_pte_at() on whatever we return, which writes out
+        * all of the sub-ptes for the hugepage range.  So we have
+        * to give it the first such sub-pte.
+        */
+       addr &= HPAGE_MASK;
+
        pgd = pgd_offset(mm, addr);
        pud = pud_alloc(mm, pgd, addr);
        if (pud) {
index 5b26af8..e112d1a 100644 (file)
@@ -1740,7 +1740,7 @@ EXPORT_SYMBOL(blk_run_queue);
 
 /**
  * blk_cleanup_queue: - release a &request_queue_t when it is no longer needed
- * @q:    the request queue to be released
+ * @kobj:    the kobj belonging of the request queue to be released
  *
  * Description:
  *     blk_cleanup_queue is the pair to blk_init_queue() or
index d6e8ffb..447d8e6 100644 (file)
@@ -25,9 +25,6 @@ obj-$(CONFIG_CONNECTOR)               += connector/
 obj-$(CONFIG_FB_I810)           += video/i810/
 obj-$(CONFIG_FB_INTEL)          += video/intelfb/
 
-# we also need input/serio early so serio bus is initialized by the time
-# serial drivers start registering their serio ports
-obj-$(CONFIG_SERIO)            += input/serio/
 obj-y                          += serial/
 obj-$(CONFIG_PARPORT)          += parport/
 obj-y                          += base/ block/ misc/ mfd/ net/ media/
@@ -53,6 +50,7 @@ obj-$(CONFIG_TC)              += tc/
 obj-$(CONFIG_USB)              += usb/
 obj-$(CONFIG_PCI)              += usb/
 obj-$(CONFIG_USB_GADGET)       += usb/gadget/
+obj-$(CONFIG_SERIO)            += input/serio/
 obj-$(CONFIG_GAMEPORT)         += input/gameport/
 obj-$(CONFIG_INPUT)            += input/
 obj-$(CONFIG_I2O)              += message/
@@ -71,6 +69,7 @@ obj-$(CONFIG_CPU_FREQ)                += cpufreq/
 obj-$(CONFIG_MMC)              += mmc/
 obj-$(CONFIG_NEW_LEDS)         += leds/
 obj-$(CONFIG_INFINIBAND)       += infiniband/
+obj-$(CONFIG_IPATH_CORE)       += infiniband/
 obj-$(CONFIG_SGI_SN)           += sn/
 obj-y                          += firmware/
 obj-$(CONFIG_CRYPTO)           += crypto/
index 9888bc1..473a13b 100644 (file)
@@ -65,7 +65,7 @@ MODULE_LICENSE("GPL");
 
 
 typedef struct bluecard_info_t {
-       dev_link_t link;
+       struct pcmcia_device *p_dev;
        dev_node_t node;
 
        struct hci_dev *hdev;
@@ -85,8 +85,8 @@ typedef struct bluecard_info_t {
 } bluecard_info_t;
 
 
-static void bluecard_config(dev_link_t *link);
-static void bluecard_release(dev_link_t *link);
+static int bluecard_config(struct pcmcia_device *link);
+static void bluecard_release(struct pcmcia_device *link);
 
 static void bluecard_detach(struct pcmcia_device *p_dev);
 
@@ -162,7 +162,7 @@ static void bluecard_detach(struct pcmcia_device *p_dev);
 static void bluecard_activity_led_timeout(u_long arg)
 {
        bluecard_info_t *info = (bluecard_info_t *)arg;
-       unsigned int iobase = info->link.io.BasePort1;
+       unsigned int iobase = info->p_dev->io.BasePort1;
 
        if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
                return;
@@ -179,7 +179,7 @@ static void bluecard_activity_led_timeout(u_long arg)
 
 static void bluecard_enable_activity_led(bluecard_info_t *info)
 {
-       unsigned int iobase = info->link.io.BasePort1;
+       unsigned int iobase = info->p_dev->io.BasePort1;
 
        if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
                return;
@@ -235,7 +235,7 @@ static void bluecard_write_wakeup(bluecard_info_t *info)
        }
 
        do {
-               register unsigned int iobase = info->link.io.BasePort1;
+               register unsigned int iobase = info->p_dev->io.BasePort1;
                register unsigned int offset;
                register unsigned char command;
                register unsigned long ready_bit;
@@ -244,7 +244,7 @@ static void bluecard_write_wakeup(bluecard_info_t *info)
 
                clear_bit(XMIT_WAKEUP, &(info->tx_state));
 
-               if (!(info->link.state & DEV_PRESENT))
+               if (!pcmcia_dev_present(info->p_dev))
                        return;
 
                if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
@@ -382,7 +382,7 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
                return;
        }
 
-       iobase = info->link.io.BasePort1;
+       iobase = info->p_dev->io.BasePort1;
 
        if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
                bluecard_enable_activity_led(info);
@@ -512,7 +512,7 @@ static irqreturn_t bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *r
        if (!test_bit(CARD_READY, &(info->hw_state)))
                return IRQ_HANDLED;
 
-       iobase = info->link.io.BasePort1;
+       iobase = info->p_dev->io.BasePort1;
 
        spin_lock(&(info->lock));
 
@@ -626,7 +626,7 @@ static int bluecard_hci_flush(struct hci_dev *hdev)
 static int bluecard_hci_open(struct hci_dev *hdev)
 {
        bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
-       unsigned int iobase = info->link.io.BasePort1;
+       unsigned int iobase = info->p_dev->io.BasePort1;
 
        if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
                bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
@@ -646,7 +646,7 @@ static int bluecard_hci_open(struct hci_dev *hdev)
 static int bluecard_hci_close(struct hci_dev *hdev)
 {
        bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
-       unsigned int iobase = info->link.io.BasePort1;
+       unsigned int iobase = info->p_dev->io.BasePort1;
 
        if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
                return 0;
@@ -713,7 +713,7 @@ static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned l
 
 static int bluecard_open(bluecard_info_t *info)
 {
-       unsigned int iobase = info->link.io.BasePort1;
+       unsigned int iobase = info->p_dev->io.BasePort1;
        struct hci_dev *hdev;
        unsigned char id;
 
@@ -831,7 +831,7 @@ static int bluecard_open(bluecard_info_t *info)
 
 static int bluecard_close(bluecard_info_t *info)
 {
-       unsigned int iobase = info->link.io.BasePort1;
+       unsigned int iobase = info->p_dev->io.BasePort1;
        struct hci_dev *hdev = info->hdev;
 
        if (!hdev)
@@ -856,17 +856,16 @@ static int bluecard_close(bluecard_info_t *info)
        return 0;
 }
 
-static int bluecard_attach(struct pcmcia_device *p_dev)
+static int bluecard_probe(struct pcmcia_device *link)
 {
        bluecard_info_t *info;
-       dev_link_t *link;
 
        /* Create new info device */
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;
 
-       link = &info->link;
+       info->p_dev = link;
        link->priv = info;
 
        link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
@@ -878,32 +877,22 @@ static int bluecard_attach(struct pcmcia_device *p_dev)
        link->irq.Instance = info;
 
        link->conf.Attributes = CONF_ENABLE_IRQ;
-       link->conf.Vcc = 50;
        link->conf.IntType = INT_MEMORY_AND_IO;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       bluecard_config(link);
-
-       return 0;
+       return bluecard_config(link);
 }
 
 
-static void bluecard_detach(struct pcmcia_device *p_dev)
+static void bluecard_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        bluecard_info_t *info = link->priv;
 
-       if (link->state & DEV_CONFIG)
-               bluecard_release(link);
-
+       bluecard_release(link);
        kfree(info);
 }
 
 
-static int first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        int i;
 
@@ -918,14 +907,12 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse
        return pcmcia_parse_tuple(handle, tuple, parse);
 }
 
-static void bluecard_config(dev_link_t *link)
+static int bluecard_config(struct pcmcia_device *link)
 {
-       client_handle_t handle = link->handle;
        bluecard_info_t *info = link->priv;
        tuple_t tuple;
        u_short buf[256];
        cisparse_t parse;
-       config_info_t config;
        int i, n, last_ret, last_fn;
 
        tuple.TupleData = (cisdata_t *)buf;
@@ -935,7 +922,7 @@ static void bluecard_config(dev_link_t *link)
 
        /* Get configuration register information */
        tuple.DesiredTuple = CISTPL_CONFIG;
-       last_ret = first_tuple(handle, &tuple, &parse);
+       last_ret = first_tuple(link, &tuple, &parse);
        if (last_ret != CS_SUCCESS) {
                last_fn = ParseTuple;
                goto cs_failed;
@@ -943,36 +930,31 @@ static void bluecard_config(dev_link_t *link)
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-       i = pcmcia_get_configuration_info(handle, &config);
-       link->conf.Vcc = config.Vcc;
-
        link->conf.ConfigIndex = 0x20;
        link->io.NumPorts1 = 64;
        link->io.IOAddrLines = 6;
 
        for (n = 0; n < 0x400; n += 0x40) {
                link->io.BasePort1 = n ^ 0x300;
-               i = pcmcia_request_io(link->handle, &link->io);
+               i = pcmcia_request_io(link, &link->io);
                if (i == CS_SUCCESS)
                        break;
        }
 
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestIO, i);
+               cs_error(link, RequestIO, i);
                goto failed;
        }
 
-       i = pcmcia_request_irq(link->handle, &link->irq);
+       i = pcmcia_request_irq(link, &link->irq);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestIRQ, i);
+               cs_error(link, RequestIRQ, i);
                link->irq.AssignedIRQ = 0;
        }
 
-       i = pcmcia_request_configuration(link->handle, &link->conf);
+       i = pcmcia_request_configuration(link, &link->conf);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestConfiguration, i);
+               cs_error(link, RequestConfiguration, i);
                goto failed;
        }
 
@@ -980,57 +962,28 @@ static void bluecard_config(dev_link_t *link)
                goto failed;
 
        strcpy(info->node.dev_name, info->hdev->name);
-       link->dev = &info->node;
-       link->state &= ~DEV_CONFIG_PENDING;
+       link->dev_node = &info->node;
 
-       return;
+       return 0;
 
 cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 
 failed:
        bluecard_release(link);
+       return -ENODEV;
 }
 
 
-static void bluecard_release(dev_link_t *link)
+static void bluecard_release(struct pcmcia_device *link)
 {
        bluecard_info_t *info = link->priv;
 
-       if (link->state & DEV_PRESENT)
-               bluecard_close(info);
+       bluecard_close(info);
 
        del_timer(&(info->timer));
 
-       link->dev = NULL;
-
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
-
-       link->state &= ~DEV_CONFIG;
-}
-
-static int bluecard_suspend(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int bluecard_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (DEV_OK(link))
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-       return 0;
+       pcmcia_disable_device(link);
 }
 
 static struct pcmcia_device_id bluecard_ids[] = {
@@ -1046,11 +999,9 @@ static struct pcmcia_driver bluecard_driver = {
        .drv            = {
                .name   = "bluecard_cs",
        },
-       .probe          = bluecard_attach,
+       .probe          = bluecard_probe,
        .remove         = bluecard_detach,
        .id_table       = bluecard_ids,
-       .suspend        = bluecard_suspend,
-       .resume         = bluecard_resume,
 };
 
 static int __init init_bluecard_cs(void)
index 7e21b1f..b94ac2f 100644 (file)
@@ -72,7 +72,7 @@ MODULE_LICENSE("GPL");
 
 
 typedef struct bt3c_info_t {
-       dev_link_t link;
+       struct pcmcia_device *p_dev;
        dev_node_t node;
 
        struct hci_dev *hdev;
@@ -88,8 +88,8 @@ typedef struct bt3c_info_t {
 } bt3c_info_t;
 
 
-static void bt3c_config(dev_link_t *link);
-static void bt3c_release(dev_link_t *link);
+static int bt3c_config(struct pcmcia_device *link);
+static void bt3c_release(struct pcmcia_device *link);
 
 static void bt3c_detach(struct pcmcia_device *p_dev);
 
@@ -191,11 +191,11 @@ static void bt3c_write_wakeup(bt3c_info_t *info)
                return;
 
        do {
-               register unsigned int iobase = info->link.io.BasePort1;
+               register unsigned int iobase = info->p_dev->io.BasePort1;
                register struct sk_buff *skb;
                register int len;
 
-               if (!(info->link.state & DEV_PRESENT))
+               if (!pcmcia_dev_present(info->p_dev))
                        break;
 
 
@@ -229,7 +229,7 @@ static void bt3c_receive(bt3c_info_t *info)
                return;
        }
 
-       iobase = info->link.io.BasePort1;
+       iobase = info->p_dev->io.BasePort1;
 
        avail = bt3c_read(iobase, 0x7006);
        //printk("bt3c_cs: receiving %d bytes\n", avail);
@@ -350,7 +350,7 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
                return IRQ_NONE;
        }
 
-       iobase = info->link.io.BasePort1;
+       iobase = info->p_dev->io.BasePort1;
 
        spin_lock(&(info->lock));
 
@@ -481,7 +481,7 @@ static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int co
        unsigned int iobase, size, addr, fcs, tmp;
        int i, err = 0;
 
-       iobase = info->link.io.BasePort1;
+       iobase = info->p_dev->io.BasePort1;
 
        /* Reset */
        bt3c_io_write(iobase, 0x8040, 0x0404);
@@ -562,7 +562,6 @@ static int bt3c_open(bt3c_info_t *info)
 {
        const struct firmware *firmware;
        struct hci_dev *hdev;
-       client_handle_t handle;
        int err;
 
        spin_lock_init(&(info->lock));
@@ -594,10 +593,8 @@ static int bt3c_open(bt3c_info_t *info)
 
        hdev->owner = THIS_MODULE;
 
-       handle = info->link.handle;
-
        /* Load firmware */
-       err = request_firmware(&firmware, "BT3CPCC.bin", &handle_to_dev(handle));
+       err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
        if (err < 0) {
                BT_ERR("Firmware request failed");
                goto error;
@@ -648,17 +645,16 @@ static int bt3c_close(bt3c_info_t *info)
        return 0;
 }
 
-static int bt3c_attach(struct pcmcia_device *p_dev)
+static int bt3c_probe(struct pcmcia_device *link)
 {
        bt3c_info_t *info;
-       dev_link_t *link;
 
        /* Create new info device */
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;
 
-       link = &info->link;
+       info->p_dev = link;
        link->priv = info;
 
        link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
@@ -670,31 +666,21 @@ static int bt3c_attach(struct pcmcia_device *p_dev)
        link->irq.Instance = info;
 
        link->conf.Attributes = CONF_ENABLE_IRQ;
-       link->conf.Vcc = 50;
        link->conf.IntType = INT_MEMORY_AND_IO;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       bt3c_config(link);
-
-       return 0;
+       return bt3c_config(link);
 }
 
 
-static void bt3c_detach(struct pcmcia_device *p_dev)
+static void bt3c_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        bt3c_info_t *info = link->priv;
 
-       if (link->state & DEV_CONFIG)
-               bt3c_release(link);
-
+       bt3c_release(link);
        kfree(info);
 }
 
-static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        int i;
 
@@ -705,30 +691,28 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
        return pcmcia_parse_tuple(handle, tuple, parse);
 }
 
-static int first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS)
                return CS_NO_MORE_ITEMS;
        return get_tuple(handle, tuple, parse);
 }
 
-static int next_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        if (pcmcia_get_next_tuple(handle, tuple) != CS_SUCCESS)
                return CS_NO_MORE_ITEMS;
        return get_tuple(handle, tuple, parse);
 }
 
-static void bt3c_config(dev_link_t *link)
+static int bt3c_config(struct pcmcia_device *link)
 {
        static kio_addr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
-       client_handle_t handle = link->handle;
        bt3c_info_t *info = link->priv;
        tuple_t tuple;
        u_short buf[256];
        cisparse_t parse;
        cistpl_cftable_entry_t *cf = &parse.cftable_entry;
-       config_info_t config;
        int i, j, try, last_ret, last_fn;
 
        tuple.TupleData = (cisdata_t *)buf;
@@ -738,7 +722,7 @@ static void bt3c_config(dev_link_t *link)
 
        /* Get configuration register information */
        tuple.DesiredTuple = CISTPL_CONFIG;
-       last_ret = first_tuple(handle, &tuple, &parse);
+       last_ret = first_tuple(link, &tuple, &parse);
        if (last_ret != CS_SUCCESS) {
                last_fn = ParseTuple;
                goto cs_failed;
@@ -746,11 +730,6 @@ static void bt3c_config(dev_link_t *link)
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-       i = pcmcia_get_configuration_info(handle, &config);
-       link->conf.Vcc = config.Vcc;
-
        /* First pass: look for a config entry that looks normal. */
        tuple.TupleData = (cisdata_t *)buf;
        tuple.TupleOffset = 0;
@@ -759,59 +738,59 @@ static void bt3c_config(dev_link_t *link)
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
        /* Two tries: without IO aliases, then with aliases */
        for (try = 0; try < 2; try++) {
-               i = first_tuple(handle, &tuple, &parse);
+               i = first_tuple(link, &tuple, &parse);
                while (i != CS_NO_MORE_ITEMS) {
                        if (i != CS_SUCCESS)
                                goto next_entry;
                        if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
-                               link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+                               link->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
                        if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) {
                                link->conf.ConfigIndex = cf->index;
                                link->io.BasePort1 = cf->io.win[0].base;
                                link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
-                               i = pcmcia_request_io(link->handle, &link->io);
+                               i = pcmcia_request_io(link, &link->io);
                                if (i == CS_SUCCESS)
                                        goto found_port;
                        }
 next_entry:
-                       i = next_tuple(handle, &tuple, &parse);
+                       i = next_tuple(link, &tuple, &parse);
                }
        }
 
        /* Second pass: try to find an entry that isn't picky about
           its base address, then try to grab any standard serial port
           address, and finally try to get any free port. */
-       i = first_tuple(handle, &tuple, &parse);
+       i = first_tuple(link, &tuple, &parse);
        while (i != CS_NO_MORE_ITEMS) {
                if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
                        link->conf.ConfigIndex = cf->index;
                        for (j = 0; j < 5; j++) {
                                link->io.BasePort1 = base[j];
                                link->io.IOAddrLines = base[j] ? 16 : 3;
-                               i = pcmcia_request_io(link->handle, &link->io);
+                               i = pcmcia_request_io(link, &link->io);
                                if (i == CS_SUCCESS)
                                        goto found_port;
                        }
                }
-               i = next_tuple(handle, &tuple, &parse);
+               i = next_tuple(link, &tuple, &parse);
        }
 
 found_port:
        if (i != CS_SUCCESS) {
                BT_ERR("No usable port range found");
-               cs_error(link->handle, RequestIO, i);
+               cs_error(link, RequestIO, i);
                goto failed;
        }
 
-       i = pcmcia_request_irq(link->handle, &link->irq);
+       i = pcmcia_request_irq(link, &link->irq);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestIRQ, i);
+               cs_error(link, RequestIRQ, i);
                link->irq.AssignedIRQ = 0;
        }
 
-       i = pcmcia_request_configuration(link->handle, &link->conf);
+       i = pcmcia_request_configuration(link, &link->conf);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestConfiguration, i);
+               cs_error(link, RequestConfiguration, i);
                goto failed;
        }
 
@@ -819,55 +798,26 @@ found_port:
                goto failed;
 
        strcpy(info->node.dev_name, info->hdev->name);
-       link->dev = &info->node;
-       link->state &= ~DEV_CONFIG_PENDING;
+       link->dev_node = &info->node;
 
-       return;
+       return 0;
 
 cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 
 failed:
        bt3c_release(link);
+       return -ENODEV;
 }
 
 
-static void bt3c_release(dev_link_t *link)
+static void bt3c_release(struct pcmcia_device *link)
 {
        bt3c_info_t *info = link->priv;
 
-       if (link->state & DEV_PRESENT)
-               bt3c_close(info);
-
-       link->dev = NULL;
-
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
-
-       link->state &= ~DEV_CONFIG;
-}
-
-static int bt3c_suspend(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
+       bt3c_close(info);
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int bt3c_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (DEV_OK(link))
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-       return 0;
+       pcmcia_disable_device(link);
 }
 
 
@@ -882,11 +832,9 @@ static struct pcmcia_driver bt3c_driver = {
        .drv            = {
                .name   = "bt3c_cs",
        },
-       .probe          = bt3c_attach,
+       .probe          = bt3c_probe,
        .remove         = bt3c_detach,
        .id_table       = bt3c_ids,
-       .suspend        = bt3c_suspend,
-       .resume         = bt3c_resume,
 };
 
 static int __init init_bt3c_cs(void)
index 7b4bff4..9ce4c93 100644 (file)
@@ -68,7 +68,7 @@ MODULE_LICENSE("GPL");
 
 
 typedef struct btuart_info_t {
-       dev_link_t link;
+       struct pcmcia_device *p_dev;
        dev_node_t node;
 
        struct hci_dev *hdev;
@@ -84,8 +84,8 @@ typedef struct btuart_info_t {
 } btuart_info_t;
 
 
-static void btuart_config(dev_link_t *link);
-static void btuart_release(dev_link_t *link);
+static int btuart_config(struct pcmcia_device *link);
+static void btuart_release(struct pcmcia_device *link);
 
 static void btuart_detach(struct pcmcia_device *p_dev);
 
@@ -146,13 +146,13 @@ static void btuart_write_wakeup(btuart_info_t *info)
        }
 
        do {
-               register unsigned int iobase = info->link.io.BasePort1;
+               register unsigned int iobase = info->p_dev->io.BasePort1;
                register struct sk_buff *skb;
                register int len;
 
                clear_bit(XMIT_WAKEUP, &(info->tx_state));
 
-               if (!(info->link.state & DEV_PRESENT))
+               if (!pcmcia_dev_present(info->p_dev))
                        return;
 
                if (!(skb = skb_dequeue(&(info->txq))))
@@ -187,7 +187,7 @@ static void btuart_receive(btuart_info_t *info)
                return;
        }
 
-       iobase = info->link.io.BasePort1;
+       iobase = info->p_dev->io.BasePort1;
 
        do {
                info->hdev->stat.byte_rx++;
@@ -301,7 +301,7 @@ static irqreturn_t btuart_interrupt(int irq, void *dev_inst, struct pt_regs *reg
                return IRQ_NONE;
        }
 
-       iobase = info->link.io.BasePort1;
+       iobase = info->p_dev->io.BasePort1;
 
        spin_lock(&(info->lock));
 
@@ -357,7 +357,7 @@ static void btuart_change_speed(btuart_info_t *info, unsigned int speed)
                return;
        }
 
-       iobase = info->link.io.BasePort1;
+       iobase = info->p_dev->io.BasePort1;
 
        spin_lock_irqsave(&(info->lock), flags);
 
@@ -481,7 +481,7 @@ static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned lon
 static int btuart_open(btuart_info_t *info)
 {
        unsigned long flags;
-       unsigned int iobase = info->link.io.BasePort1;
+       unsigned int iobase = info->p_dev->io.BasePort1;
        struct hci_dev *hdev;
 
        spin_lock_init(&(info->lock));
@@ -550,7 +550,7 @@ static int btuart_open(btuart_info_t *info)
 static int btuart_close(btuart_info_t *info)
 {
        unsigned long flags;
-       unsigned int iobase = info->link.io.BasePort1;
+       unsigned int iobase = info->p_dev->io.BasePort1;
        struct hci_dev *hdev = info->hdev;
 
        if (!hdev)
@@ -576,17 +576,16 @@ static int btuart_close(btuart_info_t *info)
        return 0;
 }
 
-static int btuart_attach(struct pcmcia_device *p_dev)
+static int btuart_probe(struct pcmcia_device *link)
 {
        btuart_info_t *info;
-       dev_link_t *link;
 
        /* Create new info device */
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;
 
-       link = &info->link;
+       info->p_dev = link;
        link->priv = info;
 
        link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
@@ -598,31 +597,21 @@ static int btuart_attach(struct pcmcia_device *p_dev)
        link->irq.Instance = info;
 
        link->conf.Attributes = CONF_ENABLE_IRQ;
-       link->conf.Vcc = 50;
        link->conf.IntType = INT_MEMORY_AND_IO;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       btuart_config(link);
-
-       return 0;
+       return btuart_config(link);
 }
 
 
-static void btuart_detach(struct pcmcia_device *p_dev)
+static void btuart_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        btuart_info_t *info = link->priv;
 
-       if (link->state & DEV_CONFIG)
-               btuart_release(link);
-
+       btuart_release(link);
        kfree(info);
 }
 
-static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        int i;
 
@@ -633,30 +622,28 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
        return pcmcia_parse_tuple(handle, tuple, parse);
 }
 
-static int first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS)
                return CS_NO_MORE_ITEMS;
        return get_tuple(handle, tuple, parse);
 }
 
-static int next_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        if (pcmcia_get_next_tuple(handle, tuple) != CS_SUCCESS)
                return CS_NO_MORE_ITEMS;
        return get_tuple(handle, tuple, parse);
 }
 
-static void btuart_config(dev_link_t *link)
+static int btuart_config(struct pcmcia_device *link)
 {
        static kio_addr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
-       client_handle_t handle = link->handle;
        btuart_info_t *info = link->priv;
        tuple_t tuple;
        u_short buf[256];
        cisparse_t parse;
        cistpl_cftable_entry_t *cf = &parse.cftable_entry;
-       config_info_t config;
        int i, j, try, last_ret, last_fn;
 
        tuple.TupleData = (cisdata_t *)buf;
@@ -666,7 +653,7 @@ static void btuart_config(dev_link_t *link)
 
        /* Get configuration register information */
        tuple.DesiredTuple = CISTPL_CONFIG;
-       last_ret = first_tuple(handle, &tuple, &parse);
+       last_ret = first_tuple(link, &tuple, &parse);
        if (last_ret != CS_SUCCESS) {
                last_fn = ParseTuple;
                goto cs_failed;
@@ -674,11 +661,6 @@ static void btuart_config(dev_link_t *link)
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-       i = pcmcia_get_configuration_info(handle, &config);
-       link->conf.Vcc = config.Vcc;
-
        /* First pass: look for a config entry that looks normal. */
        tuple.TupleData = (cisdata_t *) buf;
        tuple.TupleOffset = 0;
@@ -687,29 +669,29 @@ static void btuart_config(dev_link_t *link)
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
        /* Two tries: without IO aliases, then with aliases */
        for (try = 0; try < 2; try++) {
-               i = first_tuple(handle, &tuple, &parse);
+               i = first_tuple(link, &tuple, &parse);
                while (i != CS_NO_MORE_ITEMS) {
                        if (i != CS_SUCCESS)
                                goto next_entry;
                        if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
-                               link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+                               link->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
                        if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) {
                                link->conf.ConfigIndex = cf->index;
                                link->io.BasePort1 = cf->io.win[0].base;
                                link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
-                               i = pcmcia_request_io(link->handle, &link->io);
+                               i = pcmcia_request_io(link, &link->io);
                                if (i == CS_SUCCESS)
                                        goto found_port;
                        }
 next_entry:
-                       i = next_tuple(handle, &tuple, &parse);
+                       i = next_tuple(link, &tuple, &parse);
                }
        }
 
        /* Second pass: try to find an entry that isn't picky about
           its base address, then try to grab any standard serial port
           address, and finally try to get any free port. */
-       i = first_tuple(handle, &tuple, &parse);
+       i = first_tuple(link, &tuple, &parse);
        while (i != CS_NO_MORE_ITEMS) {
                if ((i == CS_SUCCESS) && (cf->io.nwin > 0)
                    && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
@@ -717,30 +699,30 @@ next_entry:
                        for (j = 0; j < 5; j++) {
                                link->io.BasePort1 = base[j];
                                link->io.IOAddrLines = base[j] ? 16 : 3;
-                               i = pcmcia_request_io(link->handle, &link->io);
+                               i = pcmcia_request_io(link, &link->io);
                                if (i == CS_SUCCESS)
                                        goto found_port;
                        }
                }
-               i = next_tuple(handle, &tuple, &parse);
+               i = next_tuple(link, &tuple, &parse);
        }
 
 found_port:
        if (i != CS_SUCCESS) {
                BT_ERR("No usable port range found");
-               cs_error(link->handle, RequestIO, i);
+               cs_error(link, RequestIO, i);
                goto failed;
        }
 
-       i = pcmcia_request_irq(link->handle, &link->irq);
+       i = pcmcia_request_irq(link, &link->irq);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestIRQ, i);
+               cs_error(link, RequestIRQ, i);
                link->irq.AssignedIRQ = 0;
        }
 
-       i = pcmcia_request_configuration(link->handle, &link->conf);
+       i = pcmcia_request_configuration(link, &link->conf);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestConfiguration, i);
+               cs_error(link, RequestConfiguration, i);
                goto failed;
        }
 
@@ -748,58 +730,28 @@ found_port:
                goto failed;
 
        strcpy(info->node.dev_name, info->hdev->name);
-       link->dev = &info->node;
-       link->state &= ~DEV_CONFIG_PENDING;
+       link->dev_node = &info->node;
 
-       return;
+       return 0;
 
 cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 
 failed:
        btuart_release(link);
+       return -ENODEV;
 }
 
 
-static void btuart_release(dev_link_t *link)
+static void btuart_release(struct pcmcia_device *link)
 {
        btuart_info_t *info = link->priv;
 
-       if (link->state & DEV_PRESENT)
-               btuart_close(info);
-
-       link->dev = NULL;
-
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
-
-       link->state &= ~DEV_CONFIG;
-}
-
-static int btuart_suspend(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
+       btuart_close(info);
 
-       return 0;
+       pcmcia_disable_device(link);
 }
 
-static int btuart_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (DEV_OK(link))
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-       return 0;
-}
-
-
 static struct pcmcia_device_id btuart_ids[] = {
        /* don't use this driver. Use serial_cs + hci_uart instead */
        PCMCIA_DEVICE_NULL
@@ -811,11 +763,9 @@ static struct pcmcia_driver btuart_driver = {
        .drv            = {
                .name   = "btuart_cs",
        },
-       .probe          = btuart_attach,
+       .probe          = btuart_probe,
        .remove         = btuart_detach,
        .id_table       = btuart_ids,
-       .suspend        = btuart_suspend,
-       .resume         = btuart_resume,
 };
 
 static int __init init_btuart_cs(void)
index 0449bc4..a71a240 100644 (file)
@@ -68,7 +68,7 @@ MODULE_LICENSE("GPL");
 
 
 typedef struct dtl1_info_t {
-       dev_link_t link;
+       struct pcmcia_device *p_dev;
        dev_node_t node;
 
        struct hci_dev *hdev;
@@ -87,8 +87,8 @@ typedef struct dtl1_info_t {
 } dtl1_info_t;
 
 
-static void dtl1_config(dev_link_t *link);
-static void dtl1_release(dev_link_t *link);
+static int dtl1_config(struct pcmcia_device *link);
+static void dtl1_release(struct pcmcia_device *link);
 
 static void dtl1_detach(struct pcmcia_device *p_dev);
 
@@ -153,13 +153,13 @@ static void dtl1_write_wakeup(dtl1_info_t *info)
        }
 
        do {
-               register unsigned int iobase = info->link.io.BasePort1;
+               register unsigned int iobase = info->p_dev->io.BasePort1;
                register struct sk_buff *skb;
                register int len;
 
                clear_bit(XMIT_WAKEUP, &(info->tx_state));
 
-               if (!(info->link.state & DEV_PRESENT))
+               if (!pcmcia_dev_present(info->p_dev))
                        return;
 
                if (!(skb = skb_dequeue(&(info->txq))))
@@ -218,7 +218,7 @@ static void dtl1_receive(dtl1_info_t *info)
                return;
        }
 
-       iobase = info->link.io.BasePort1;
+       iobase = info->p_dev->io.BasePort1;
 
        do {
                info->hdev->stat.byte_rx++;
@@ -305,7 +305,7 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
                return IRQ_NONE;
        }
 
-       iobase = info->link.io.BasePort1;
+       iobase = info->p_dev->io.BasePort1;
 
        spin_lock(&(info->lock));
 
@@ -458,7 +458,7 @@ static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd,  unsigned long
 static int dtl1_open(dtl1_info_t *info)
 {
        unsigned long flags;
-       unsigned int iobase = info->link.io.BasePort1;
+       unsigned int iobase = info->p_dev->io.BasePort1;
        struct hci_dev *hdev;
 
        spin_lock_init(&(info->lock));
@@ -504,7 +504,7 @@ static int dtl1_open(dtl1_info_t *info)
        outb(UART_LCR_WLEN8, iobase + UART_LCR);        /* Reset DLAB */
        outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
 
-       info->ri_latch = inb(info->link.io.BasePort1 + UART_MSR) & UART_MSR_RI;
+       info->ri_latch = inb(info->p_dev->io.BasePort1 + UART_MSR) & UART_MSR_RI;
 
        /* Turn on interrupts */
        outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
@@ -529,7 +529,7 @@ static int dtl1_open(dtl1_info_t *info)
 static int dtl1_close(dtl1_info_t *info)
 {
        unsigned long flags;
-       unsigned int iobase = info->link.io.BasePort1;
+       unsigned int iobase = info->p_dev->io.BasePort1;
        struct hci_dev *hdev = info->hdev;
 
        if (!hdev)
@@ -555,17 +555,16 @@ static int dtl1_close(dtl1_info_t *info)
        return 0;
 }
 
-static int dtl1_attach(struct pcmcia_device *p_dev)
+static int dtl1_probe(struct pcmcia_device *link)
 {
        dtl1_info_t *info;
-       dev_link_t *link;
 
        /* Create new info device */
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;
 
-       link = &info->link;
+       info->p_dev = link;
        link->priv = info;
 
        link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
@@ -577,31 +576,22 @@ static int dtl1_attach(struct pcmcia_device *p_dev)
        link->irq.Instance = info;
 
        link->conf.Attributes = CONF_ENABLE_IRQ;
-       link->conf.Vcc = 50;
        link->conf.IntType = INT_MEMORY_AND_IO;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       dtl1_config(link);
-
-       return 0;
+       return dtl1_config(link);
 }
 
 
-static void dtl1_detach(struct pcmcia_device *p_dev)
+static void dtl1_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        dtl1_info_t *info = link->priv;
 
-       if (link->state & DEV_CONFIG)
-               dtl1_release(link);
+       dtl1_release(link);
 
        kfree(info);
 }
 
-static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        int i;
 
@@ -612,29 +602,27 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
        return pcmcia_parse_tuple(handle, tuple, parse);
 }
 
-static int first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS)
                return CS_NO_MORE_ITEMS;
        return get_tuple(handle, tuple, parse);
 }
 
-static int next_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        if (pcmcia_get_next_tuple(handle, tuple) != CS_SUCCESS)
                return CS_NO_MORE_ITEMS;
        return get_tuple(handle, tuple, parse);
 }
 
-static void dtl1_config(dev_link_t *link)
+static int dtl1_config(struct pcmcia_device *link)
 {
-       client_handle_t handle = link->handle;
        dtl1_info_t *info = link->priv;
        tuple_t tuple;
        u_short buf[256];
        cisparse_t parse;
        cistpl_cftable_entry_t *cf = &parse.cftable_entry;
-       config_info_t config;
        int i, last_ret, last_fn;
 
        tuple.TupleData = (cisdata_t *)buf;
@@ -644,7 +632,7 @@ static void dtl1_config(dev_link_t *link)
 
        /* Get configuration register information */
        tuple.DesiredTuple = CISTPL_CONFIG;
-       last_ret = first_tuple(handle, &tuple, &parse);
+       last_ret = first_tuple(link, &tuple, &parse);
        if (last_ret != CS_SUCCESS) {
                last_fn = ParseTuple;
                goto cs_failed;
@@ -652,11 +640,6 @@ static void dtl1_config(dev_link_t *link)
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-       i = pcmcia_get_configuration_info(handle, &config);
-       link->conf.Vcc = config.Vcc;
-
        tuple.TupleData = (cisdata_t *)buf;
        tuple.TupleOffset = 0;
        tuple.TupleDataMax = 255;
@@ -665,34 +648,34 @@ static void dtl1_config(dev_link_t *link)
 
        /* Look for a generic full-sized window */
        link->io.NumPorts1 = 8;
-       i = first_tuple(handle, &tuple, &parse);
+       i = first_tuple(link, &tuple, &parse);
        while (i != CS_NO_MORE_ITEMS) {
                if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
                        link->conf.ConfigIndex = cf->index;
                        link->io.BasePort1 = cf->io.win[0].base;
                        link->io.NumPorts1 = cf->io.win[0].len; /*yo */
                        link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
-                       i = pcmcia_request_io(link->handle, &link->io);
+                       i = pcmcia_request_io(link, &link->io);
                        if (i == CS_SUCCESS)
                                break;
                }
-               i = next_tuple(handle, &tuple, &parse);
+               i = next_tuple(link, &tuple, &parse);
        }
 
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestIO, i);
+               cs_error(link, RequestIO, i);
                goto failed;
        }
 
-       i = pcmcia_request_irq(link->handle, &link->irq);
+       i = pcmcia_request_irq(link, &link->irq);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestIRQ, i);
+               cs_error(link, RequestIRQ, i);
                link->irq.AssignedIRQ = 0;
        }
 
-       i = pcmcia_request_configuration(link->handle, &link->conf);
+       i = pcmcia_request_configuration(link, &link->conf);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestConfiguration, i);
+               cs_error(link, RequestConfiguration, i);
                goto failed;
        }
 
@@ -700,55 +683,26 @@ static void dtl1_config(dev_link_t *link)
                goto failed;
 
        strcpy(info->node.dev_name, info->hdev->name);
-       link->dev = &info->node;
-       link->state &= ~DEV_CONFIG_PENDING;
+       link->dev_node = &info->node;
 
-       return;
+       return 0;
 
 cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 
 failed:
        dtl1_release(link);
+       return -ENODEV;
 }
 
 
-static void dtl1_release(dev_link_t *link)
+static void dtl1_release(struct pcmcia_device *link)
 {
        dtl1_info_t *info = link->priv;
 
-       if (link->state & DEV_PRESENT)
-               dtl1_close(info);
-
-       link->dev = NULL;
-
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
-
-       link->state &= ~DEV_CONFIG;
-}
-
-static int dtl1_suspend(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int dtl1_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
+       dtl1_close(info);
 
-       link->state &= ~DEV_SUSPEND;
-       if (DEV_OK(link))
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-       return 0;
+       pcmcia_disable_device(link);
 }
 
 
@@ -765,11 +719,9 @@ static struct pcmcia_driver dtl1_driver = {
        .drv            = {
                .name   = "dtl1_cs",
        },
-       .probe          = dtl1_attach,
+       .probe          = dtl1_probe,
        .remove         = dtl1_detach,
        .id_table       = dtl1_ids,
-       .suspend        = dtl1_suspend,
-       .resume         = dtl1_resume,
 };
 
 static int __init init_dtl1_cs(void)
index 327b00c..8d97b39 100644 (file)
@@ -904,7 +904,7 @@ static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address,
                 * It is possible the vty-server was removed after the irq was
                 * requested but before we have time to enable interrupts.
                 */
-               if (vio_enable_interrupts(vdev) == H_Success)
+               if (vio_enable_interrupts(vdev) == H_SUCCESS)
                        return 0;
                else {
                        printk(KERN_ERR "HVCS: int enable failed for"
index 8b603b2..935670a 100644 (file)
@@ -74,7 +74,7 @@ void compute_shiftstate(void);
        k_self,         k_fn,           k_spec,         k_pad,\
        k_dead,         k_cons,         k_cur,          k_shift,\
        k_meta,         k_ascii,        k_lock,         k_lowercase,\
-       k_slock,        k_dead2,        k_ignore,       k_ignore
+       k_slock,        k_dead2,        k_brl,          k_ignore
 
 typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value,
                            char up_flag, struct pt_regs *regs);
@@ -100,7 +100,7 @@ static fn_handler_fn *fn_handler[] = { FN_HANDLERS };
 const int max_vals[] = {
        255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1,
        NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1,
-       255, NR_LOCK - 1, 255
+       255, NR_LOCK - 1, 255, NR_BRL - 1
 };
 
 const int NR_TYPES = ARRAY_SIZE(max_vals);
@@ -126,7 +126,7 @@ static unsigned long key_down[NBITS(KEY_MAX)];              /* keyboard key bitmap */
 static unsigned char shift_down[NR_SHIFT];             /* shift state counters.. */
 static int dead_key_next;
 static int npadch = -1;                                        /* -1 or number assembled on pad */
-static unsigned char diacr;
+static unsigned int diacr;
 static char rep;                                       /* flag telling character repeat */
 
 static unsigned char ledstate = 0xff;                  /* undefined */
@@ -394,22 +394,30 @@ void compute_shiftstate(void)
  * Otherwise, conclude that DIACR was not combining after all,
  * queue it and return CH.
  */
-static unsigned char handle_diacr(struct vc_data *vc, unsigned char ch)
+static unsigned int handle_diacr(struct vc_data *vc, unsigned int ch)
 {
-       int d = diacr;
+       unsigned int d = diacr;
        unsigned int i;
 
        diacr = 0;
 
-       for (i = 0; i < accent_table_size; i++) {
-               if (accent_table[i].diacr == d && accent_table[i].base == ch)
-                       return accent_table[i].result;
+       if ((d & ~0xff) == BRL_UC_ROW) {
+               if ((ch & ~0xff) == BRL_UC_ROW)
+                       return d | ch;
+       } else {
+               for (i = 0; i < accent_table_size; i++)
+                       if (accent_table[i].diacr == d && accent_table[i].base == ch)
+                               return accent_table[i].result;
        }
 
-       if (ch == ' ' || ch == d)
+       if (ch == ' ' || ch == (BRL_UC_ROW|0) || ch == d)
                return d;
 
-       put_queue(vc, d);
+       if (kbd->kbdmode == VC_UNICODE)
+               to_utf8(vc, d);
+       else if (d < 0x100)
+               put_queue(vc, d);
+
        return ch;
 }
 
@@ -419,7 +427,10 @@ static unsigned char handle_diacr(struct vc_data *vc, unsigned char ch)
 static void fn_enter(struct vc_data *vc, struct pt_regs *regs)
 {
        if (diacr) {
-               put_queue(vc, diacr);
+               if (kbd->kbdmode == VC_UNICODE)
+                       to_utf8(vc, diacr);
+               else if (diacr < 0x100)
+                       put_queue(vc, diacr);
                diacr = 0;
        }
        put_queue(vc, 13);
@@ -615,7 +626,7 @@ static void k_lowercase(struct vc_data *vc, unsigned char value, char up_flag, s
        printk(KERN_ERR "keyboard.c: k_lowercase was called - impossible\n");
 }
 
-static void k_self(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs)
+static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag, struct pt_regs *regs)
 {
        if (up_flag)
                return;         /* no action, if this is a key release */
@@ -628,7 +639,10 @@ static void k_self(struct vc_data *vc, unsigned char value, char up_flag, struct
                diacr = value;
                return;
        }
-       put_queue(vc, value);
+       if (kbd->kbdmode == VC_UNICODE)
+               to_utf8(vc, value);
+       else if (value < 0x100)
+               put_queue(vc, value);
 }
 
 /*
@@ -636,13 +650,23 @@ static void k_self(struct vc_data *vc, unsigned char value, char up_flag, struct
  * dead keys modifying the same character. Very useful
  * for Vietnamese.
  */
-static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs)
+static void k_deadunicode(struct vc_data *vc, unsigned int value, char up_flag, struct pt_regs *regs)
 {
        if (up_flag)
                return;
        diacr = (diacr ? handle_diacr(vc, value) : value);
 }
 
+static void k_self(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs)
+{
+       k_unicode(vc, value, up_flag, regs);
+}
+
+static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs)
+{
+       k_deadunicode(vc, value, up_flag, regs);
+}
+
 /*
  * Obsolete - for backwards compatibility only
  */
@@ -650,7 +674,7 @@ static void k_dead(struct vc_data *vc, unsigned char value, char up_flag, struct
 {
        static unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' };
        value = ret_diacr[value];
-       k_dead2(vc, value, up_flag, regs);
+       k_deadunicode(vc, value, up_flag, regs);
 }
 
 static void k_cons(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs)
@@ -835,6 +859,62 @@ static void k_slock(struct vc_data *vc, unsigned char value, char up_flag, struc
        }
 }
 
+/* by default, 300ms interval for combination release */
+static long brl_timeout = 300;
+MODULE_PARM_DESC(brl_timeout, "Braille keys release delay in ms (0 for combination on first release, < 0 for dead characters)");
+module_param(brl_timeout, long, 0644);
+static void k_brl(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs)
+{
+       static unsigned pressed,committing;
+       static unsigned long releasestart;
+
+       if (kbd->kbdmode != VC_UNICODE) {
+               if (!up_flag)
+                       printk("keyboard mode must be unicode for braille patterns\n");
+               return;
+       }
+
+       if (!value) {
+               k_unicode(vc, BRL_UC_ROW, up_flag, regs);
+               return;
+       }
+
+       if (value > 8)
+               return;
+
+       if (brl_timeout < 0) {
+               k_deadunicode(vc, BRL_UC_ROW | (1 << (value - 1)), up_flag, regs);
+               return;
+       }
+
+       if (up_flag) {
+               if (brl_timeout) {
+                       if (!committing ||
+                           jiffies - releasestart > (brl_timeout * HZ) / 1000) {
+                               committing = pressed;
+                               releasestart = jiffies;
+                       }
+                       pressed &= ~(1 << (value - 1));
+                       if (!pressed) {
+                               if (committing) {
+                                       k_unicode(vc, BRL_UC_ROW | committing, 0, regs);
+                                       committing = 0;
+                               }
+                       }
+               } else {
+                       if (committing) {
+                               k_unicode(vc, BRL_UC_ROW | committing, 0, regs);
+                               committing = 0;
+                       }
+                       pressed &= ~(1 << (value - 1));
+               }
+       } else {
+               pressed |= 1 << (value - 1);
+               if (!brl_timeout)
+                       committing = pressed;
+       }
+}
+
 /*
  * The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
  * or (ii) whatever pattern of lights people want to show using KDSETLED,
@@ -1125,9 +1205,13 @@ static void kbd_keycode(unsigned int keycode, int down,
        }
 
        if (keycode > NR_KEYS)
-               return;
+               if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8)
+                       keysym = K(KT_BRL, keycode - KEY_BRL_DOT1 + 1);
+               else
+                       return;
+       else
+               keysym = key_map[keycode];
 
-       keysym = key_map[keycode];
        type = KTYP(keysym);
 
        if (type < 0xf0) {
index 5fdf185..02114a0 100644 (file)
@@ -46,7 +46,7 @@
 /* #define ATR_CSUM */
 
 #ifdef PCMCIA_DEBUG
-#define reader_to_dev(x)       (&handle_to_dev(x->link.handle))
+#define reader_to_dev(x)       (&handle_to_dev(x->p_dev->handle))
 static int pc_debug = PCMCIA_DEBUG;
 module_param(pc_debug, int, 0600);
 #define DEBUGP(n, rdr, x, args...) do {                                \
@@ -67,7 +67,7 @@ static char *version = "cm4000_cs.c v2.4.0gm6 - All bugs added by Harald Welte";
 #define        T_100MSEC       msecs_to_jiffies(100)
 #define        T_500MSEC       msecs_to_jiffies(500)
 
-static void cm4000_release(dev_link_t *link);
+static void cm4000_release(struct pcmcia_device *link);
 
 static int major;              /* major number we get from the kernel */
 
@@ -106,7 +106,7 @@ static int major;           /* major number we get from the kernel */
 #define REG_STOPBITS(x)                (x + 7)
 
 struct cm4000_dev {
-       dev_link_t link;                /* pcmcia link */
+       struct pcmcia_device *p_dev;
        dev_node_t node;                /* OS node (major,minor) */
 
        unsigned char atr[MAX_ATR];
@@ -149,14 +149,14 @@ struct cm4000_dev {
 #define        ZERO_DEV(dev)                                           \
        memset(&dev->atr_csum,0,                                \
                sizeof(struct cm4000_dev) -                     \
-               /*link*/ sizeof(dev_link_t) -                   \
+               /*link*/ sizeof(struct pcmcia_device) -         \
                /*node*/ sizeof(dev_node_t) -                   \
                /*atr*/ MAX_ATR*sizeof(char) -                  \
                /*rbuf*/ 512*sizeof(char) -                     \
                /*sbuf*/ 512*sizeof(char) -                     \
                /*queue*/ 4*sizeof(wait_queue_head_t))
 
-static dev_link_t *dev_table[CM4000_MAX_DEV];
+static struct pcmcia_device *dev_table[CM4000_MAX_DEV];
 static struct class *cmm_class;
 
 /* This table doesn't use spaces after the comma between fields and thus
@@ -454,7 +454,7 @@ static struct card_fixup card_fixups[] = {
 static void set_cardparameter(struct cm4000_dev *dev)
 {
        int i;
-       ioaddr_t iobase = dev->link.io.BasePort1;
+       ioaddr_t iobase = dev->p_dev->io.BasePort1;
        u_int8_t stopbits = 0x02; /* ISO default */
 
        DEBUGP(3, dev, "-> set_cardparameter\n");
@@ -487,7 +487,7 @@ static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq)
        unsigned short num_bytes_read;
        unsigned char pts_reply[4];
        ssize_t rc;
-       ioaddr_t iobase = dev->link.io.BasePort1;
+       ioaddr_t iobase = dev->p_dev->io.BasePort1;
 
        rc = 0;
 
@@ -699,7 +699,7 @@ static void terminate_monitor(struct cm4000_dev *dev)
 static void monitor_card(unsigned long p)
 {
        struct cm4000_dev *dev = (struct cm4000_dev *) p;
-       ioaddr_t iobase = dev->link.io.BasePort1;
+       ioaddr_t iobase = dev->p_dev->io.BasePort1;
        unsigned short s;
        struct ptsreq ptsreq;
        int i, atrc;
@@ -962,7 +962,7 @@ static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count,
                        loff_t *ppos)
 {
        struct cm4000_dev *dev = filp->private_data;
-       ioaddr_t iobase = dev->link.io.BasePort1;
+       ioaddr_t iobase = dev->p_dev->io.BasePort1;
        ssize_t rc;
        int i, j, k;
 
@@ -971,7 +971,7 @@ static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count,
        if (count == 0)         /* according to manpage */
                return 0;
 
-       if ((dev->link.state & DEV_PRESENT) == 0 ||     /* socket removed */
+       if (!pcmcia_dev_present(dev->p_dev) || /* device removed */
            test_bit(IS_CMM_ABSENT, &dev->flags))
                return -ENODEV;
 
@@ -1083,7 +1083,7 @@ static ssize_t cmm_write(struct file *filp, const char __user *buf,
                         size_t count, loff_t *ppos)
 {
        struct cm4000_dev *dev = (struct cm4000_dev *) filp->private_data;
-       ioaddr_t iobase = dev->link.io.BasePort1;
+       ioaddr_t iobase = dev->p_dev->io.BasePort1;
        unsigned short s;
        unsigned char tmp;
        unsigned char infolen;
@@ -1108,7 +1108,7 @@ static ssize_t cmm_write(struct file *filp, const char __user *buf,
 
        sendT0 = dev->proto ? 0 : nr > 5 ? 0x08 : 0;
 
-       if ((dev->link.state & DEV_PRESENT) == 0 ||     /* socket removed */
+       if (!pcmcia_dev_present(dev->p_dev) || /* device removed */
            test_bit(IS_CMM_ABSENT, &dev->flags))
                return -ENODEV;
 
@@ -1440,8 +1440,8 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
                     unsigned long arg)
 {
        struct cm4000_dev *dev = filp->private_data;
-       ioaddr_t iobase = dev->link.io.BasePort1;
-       dev_link_t *link;
+       ioaddr_t iobase = dev->p_dev->io.BasePort1;
+       struct pcmcia_device *link;
        int size;
        int rc;
        void __user *argp = (void __user *)arg;
@@ -1458,7 +1458,7 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
               iminor(inode), ioctl_names[_IOC_NR(cmd)]);
 
        link = dev_table[iminor(inode)];
-       if (!(DEV_OK(link))) {
+       if (!pcmcia_dev_present(link)) {
                DEBUGP(4, dev, "DEV_OK false\n");
                return -ENODEV;
        }
@@ -1660,14 +1660,14 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
 static int cmm_open(struct inode *inode, struct file *filp)
 {
        struct cm4000_dev *dev;
-       dev_link_t *link;
+       struct pcmcia_device *link;
        int rc, minor = iminor(inode);
 
        if (minor >= CM4000_MAX_DEV)
                return -ENODEV;
 
        link = dev_table[minor];
-       if (link == NULL || !(DEV_OK(link)))
+       if (link == NULL || !pcmcia_dev_present(link))
                return -ENODEV;
 
        if (link->open)
@@ -1709,7 +1709,7 @@ static int cmm_open(struct inode *inode, struct file *filp)
 static int cmm_close(struct inode *inode, struct file *filp)
 {
        struct cm4000_dev *dev;
-       dev_link_t *link;
+       struct pcmcia_device *link;
        int minor = iminor(inode);
 
        if (minor >= CM4000_MAX_DEV)
@@ -1735,7 +1735,7 @@ static int cmm_close(struct inode *inode, struct file *filp)
        return 0;
 }
 
-static void cmm_cm4000_release(dev_link_t * link)
+static void cmm_cm4000_release(struct pcmcia_device * link)
 {
        struct cm4000_dev *dev = link->priv;
 
@@ -1759,13 +1759,11 @@ static void cmm_cm4000_release(dev_link_t * link)
 
 /*==== Interface to PCMCIA Layer =======================================*/
 
-static void cm4000_config(dev_link_t * link, int devno)
+static int cm4000_config(struct pcmcia_device * link, int devno)
 {
-       client_handle_t handle = link->handle;
        struct cm4000_dev *dev;
        tuple_t tuple;
        cisparse_t parse;
-       config_info_t conf;
        u_char buf[64];
        int fail_fn, fail_rc;
        int rc;
@@ -1777,41 +1775,34 @@ static void cm4000_config(dev_link_t * link, int devno)
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
 
-       if ((fail_rc = pcmcia_get_first_tuple(handle, &tuple)) != CS_SUCCESS) {
+       if ((fail_rc = pcmcia_get_first_tuple(link, &tuple)) != CS_SUCCESS) {
                fail_fn = GetFirstTuple;
                goto cs_failed;
        }
-       if ((fail_rc = pcmcia_get_tuple_data(handle, &tuple)) != CS_SUCCESS) {
+       if ((fail_rc = pcmcia_get_tuple_data(link, &tuple)) != CS_SUCCESS) {
                fail_fn = GetTupleData;
                goto cs_failed;
        }
        if ((fail_rc =
-            pcmcia_parse_tuple(handle, &tuple, &parse)) != CS_SUCCESS) {
+            pcmcia_parse_tuple(link, &tuple, &parse)) != CS_SUCCESS) {
                fail_fn = ParseTuple;
                goto cs_failed;
        }
-       if ((fail_rc =
-            pcmcia_get_configuration_info(handle, &conf)) != CS_SUCCESS) {
-               fail_fn = GetConfigurationInfo;
-               goto cs_failed;
-       }
 
-       link->state |= DEV_CONFIG;
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
-       link->conf.Vcc = conf.Vcc;
 
        link->io.BasePort2 = 0;
        link->io.NumPorts2 = 0;
        link->io.Attributes2 = 0;
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       for (rc = pcmcia_get_first_tuple(handle, &tuple);
-            rc == CS_SUCCESS; rc = pcmcia_get_next_tuple(handle, &tuple)) {
+       for (rc = pcmcia_get_first_tuple(link, &tuple);
+            rc == CS_SUCCESS; rc = pcmcia_get_next_tuple(link, &tuple)) {
 
-               rc = pcmcia_get_tuple_data(handle, &tuple);
+               rc = pcmcia_get_tuple_data(link, &tuple);
                if (rc != CS_SUCCESS)
                        continue;
-               rc = pcmcia_parse_tuple(handle, &tuple, &parse);
+               rc = pcmcia_parse_tuple(link, &tuple, &parse);
                if (rc != CS_SUCCESS)
                        continue;
 
@@ -1831,7 +1822,7 @@ static void cm4000_config(dev_link_t * link, int devno)
                link->io.IOAddrLines = parse.cftable_entry.io.flags
                    & CISTPL_IO_LINES_MASK;
 
-               rc = pcmcia_request_io(handle, &link->io);
+               rc = pcmcia_request_io(link, &link->io);
                if (rc == CS_SUCCESS)
                        break;  /* we are done */
        }
@@ -1841,7 +1832,7 @@ static void cm4000_config(dev_link_t * link, int devno)
        link->conf.IntType = 00000002;
 
        if ((fail_rc =
-            pcmcia_request_configuration(handle, &link->conf)) != CS_SUCCESS) {
+            pcmcia_request_configuration(link, &link->conf)) != CS_SUCCESS) {
                fail_fn = RequestConfiguration;
                goto cs_release;
        }
@@ -1851,63 +1842,48 @@ static void cm4000_config(dev_link_t * link, int devno)
        dev->node.major = major;
        dev->node.minor = devno;
        dev->node.next = NULL;
-       link->dev = &dev->node;
-       link->state &= ~DEV_CONFIG_PENDING;
+       link->dev_node = &dev->node;
 
-       return;
+       return 0;
 
 cs_failed:
-       cs_error(handle, fail_fn, fail_rc);
+       cs_error(link, fail_fn, fail_rc);
 cs_release:
        cm4000_release(link);
-
-       link->state &= ~DEV_CONFIG_PENDING;
+       return -ENODEV;
 }
 
-static int cm4000_suspend(struct pcmcia_device *p_dev)
+static int cm4000_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct cm4000_dev *dev;
 
        dev = link->priv;
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
        stop_monitor(dev);
 
        return 0;
 }
 
-static int cm4000_resume(struct pcmcia_device *p_dev)
+static int cm4000_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct cm4000_dev *dev;
 
        dev = link->priv;
-
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_request_configuration(link->handle, &link->conf);
-
        if (link->open)
                start_monitor(dev);
 
        return 0;
 }
 
-static void cm4000_release(dev_link_t *link)
+static void cm4000_release(struct pcmcia_device *link)
 {
        cmm_cm4000_release(link->priv); /* delay release until device closed */
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
+       pcmcia_disable_device(link);
 }
 
-static int cm4000_attach(struct pcmcia_device *p_dev)
+static int cm4000_probe(struct pcmcia_device *link)
 {
        struct cm4000_dev *dev;
-       dev_link_t *link;
-       int i;
+       int i, ret;
 
        for (i = 0; i < CM4000_MAX_DEV; i++)
                if (dev_table[i] == NULL)
@@ -1923,7 +1899,7 @@ static int cm4000_attach(struct pcmcia_device *p_dev)
        if (dev == NULL)
                return -ENOMEM;
 
-       link = &dev->link;
+       dev->p_dev = link;
        link->priv = dev;
        link->conf.IntType = INT_MEMORY_AND_IO;
        dev_table[i] = link;
@@ -1933,11 +1909,9 @@ static int cm4000_attach(struct pcmcia_device *p_dev)
        init_waitqueue_head(&dev->atrq);
        init_waitqueue_head(&dev->readq);
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       cm4000_config(link, i);
+       ret = cm4000_config(link, i);
+       if (ret)
+               return ret;
 
        class_device_create(cmm_class, NULL, MKDEV(major, i), NULL,
                            "cmm%d", i);
@@ -1945,9 +1919,8 @@ static int cm4000_attach(struct pcmcia_device *p_dev)
        return 0;
 }
 
-static void cm4000_detach(struct pcmcia_device *p_dev)
+static void cm4000_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct cm4000_dev *dev = link->priv;
        int devno;
 
@@ -1958,11 +1931,9 @@ static void cm4000_detach(struct pcmcia_device *p_dev)
        if (devno == CM4000_MAX_DEV)
                return;
 
-       link->state &= ~DEV_PRESENT;
        stop_monitor(dev);
 
-       if (link->state & DEV_CONFIG)
-               cm4000_release(link);
+       cm4000_release(link);
 
        dev_table[devno] = NULL;
        kfree(dev);
@@ -1993,7 +1964,7 @@ static struct pcmcia_driver cm4000_driver = {
        .drv      = {
                .name = "cm4000_cs",
                },
-       .probe    = cm4000_attach,
+       .probe    = cm4000_probe,
        .remove   = cm4000_detach,
        .suspend  = cm4000_suspend,
        .resume   = cm4000_resume,
index 466e33b..29efa64 100644 (file)
@@ -41,7 +41,7 @@
 
 
 #ifdef PCMCIA_DEBUG
-#define reader_to_dev(x)       (&handle_to_dev(x->link.handle))
+#define reader_to_dev(x)       (&handle_to_dev(x->p_dev->handle))
 static int pc_debug = PCMCIA_DEBUG;
 module_param(pc_debug, int, 0600);
 #define DEBUGP(n, rdr, x, args...) do {                                \
@@ -65,7 +65,7 @@ static char *version =
 /* how often to poll for fifo status change */
 #define POLL_PERIOD                            msecs_to_jiffies(10)
 
-static void reader_release(dev_link_t *link);
+static void reader_release(struct pcmcia_device *link);
 
 static int major;
 static struct class *cmx_class;
@@ -74,7 +74,7 @@ static struct class *cmx_class;
 #define                BS_WRITABLE     0x02
 
 struct reader_dev {
-       dev_link_t              link;
+       struct pcmcia_device    *p_dev;
        dev_node_t              node;
        wait_queue_head_t       devq;
        wait_queue_head_t       poll_wait;
@@ -87,7 +87,7 @@ struct reader_dev {
        struct timer_list       poll_timer;
 };
 
-static dev_link_t *dev_table[CM_MAX_DEV];
+static struct pcmcia_device *dev_table[CM_MAX_DEV];
 
 #ifndef PCMCIA_DEBUG
 #define        xoutb   outb
@@ -116,7 +116,7 @@ static inline unsigned char xinb(unsigned short port)
 static void cm4040_do_poll(unsigned long dummy)
 {
        struct reader_dev *dev = (struct reader_dev *) dummy;
-       unsigned int obs = xinb(dev->link.io.BasePort1
+       unsigned int obs = xinb(dev->p_dev->io.BasePort1
                                + REG_OFFSET_BUFFER_STATUS);
 
        if ((obs & BSR_BULK_IN_FULL)) {
@@ -147,7 +147,7 @@ static void cm4040_stop_poll(struct reader_dev *dev)
 static int wait_for_bulk_out_ready(struct reader_dev *dev)
 {
        int i, rc;
-       int iobase = dev->link.io.BasePort1;
+       int iobase = dev->p_dev->io.BasePort1;
 
        for (i = 0; i < POLL_LOOP_COUNT; i++) {
                if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
@@ -177,7 +177,7 @@ static int wait_for_bulk_out_ready(struct reader_dev *dev)
 /* Write to Sync Control Register */
 static int write_sync_reg(unsigned char val, struct reader_dev *dev)
 {
-       int iobase = dev->link.io.BasePort1;
+       int iobase = dev->p_dev->io.BasePort1;
        int rc;
 
        rc = wait_for_bulk_out_ready(dev);
@@ -195,7 +195,7 @@ static int write_sync_reg(unsigned char val, struct reader_dev *dev)
 static int wait_for_bulk_in_ready(struct reader_dev *dev)
 {
        int i, rc;
-       int iobase = dev->link.io.BasePort1;
+       int iobase = dev->p_dev->io.BasePort1;
 
        for (i = 0; i < POLL_LOOP_COUNT; i++) {
                if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
@@ -225,7 +225,7 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf,
                        size_t count, loff_t *ppos)
 {
        struct reader_dev *dev = filp->private_data;
-       int iobase = dev->link.io.BasePort1;
+       int iobase = dev->p_dev->io.BasePort1;
        size_t bytes_to_read;
        unsigned long i;
        size_t min_bytes_to_read;
@@ -246,7 +246,7 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf,
                return -EAGAIN;
        }
 
-       if ((dev->link.state & DEV_PRESENT)==0)
+       if (!pcmcia_dev_present(dev->p_dev))
                return -ENODEV;
 
        for (i = 0; i < 5; i++) {
@@ -328,7 +328,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf,
                         size_t count, loff_t *ppos)
 {
        struct reader_dev *dev = filp->private_data;
-       int iobase = dev->link.io.BasePort1;
+       int iobase = dev->p_dev->io.BasePort1;
        ssize_t rc;
        int i;
        unsigned int bytes_to_write;
@@ -351,7 +351,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf,
                return -EAGAIN;
        }
 
-       if ((dev->link.state & DEV_PRESENT) == 0)
+       if (!pcmcia_dev_present(dev->p_dev))
                return -ENODEV;
 
        bytes_to_write = count;
@@ -445,14 +445,14 @@ static unsigned int cm4040_poll(struct file *filp, poll_table *wait)
 static int cm4040_open(struct inode *inode, struct file *filp)
 {
        struct reader_dev *dev;
-       dev_link_t *link;
+       struct pcmcia_device *link;
        int minor = iminor(inode);
 
        if (minor >= CM_MAX_DEV)
                return -ENODEV;
 
        link = dev_table[minor];
-       if (link == NULL || !(DEV_OK(link)))
+       if (link == NULL || !pcmcia_dev_present(link))
                return -ENODEV;
 
        if (link->open)
@@ -478,7 +478,7 @@ static int cm4040_open(struct inode *inode, struct file *filp)
 static int cm4040_close(struct inode *inode, struct file *filp)
 {
        struct reader_dev *dev = filp->private_data;
-       dev_link_t *link;
+       struct pcmcia_device *link;
        int minor = iminor(inode);
 
        DEBUGP(2, dev, "-> cm4040_close(maj/min=%d.%d)\n", imajor(inode),
@@ -500,7 +500,7 @@ static int cm4040_close(struct inode *inode, struct file *filp)
        return 0;
 }
 
-static void cm4040_reader_release(dev_link_t *link)
+static void cm4040_reader_release(struct pcmcia_device *link)
 {
        struct reader_dev *dev = link->priv;
 
@@ -514,60 +514,49 @@ static void cm4040_reader_release(dev_link_t *link)
        return;
 }
 
-static void reader_config(dev_link_t *link, int devno)
+static int reader_config(struct pcmcia_device *link, int devno)
 {
-       client_handle_t handle;
        struct reader_dev *dev;
        tuple_t tuple;
        cisparse_t parse;
-       config_info_t conf;
        u_char buf[64];
        int fail_fn, fail_rc;
        int rc;
 
-       handle = link->handle;
-
        tuple.DesiredTuple = CISTPL_CONFIG;
        tuple.Attributes = 0;
        tuple.TupleData = buf;
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
 
-       if ((fail_rc = pcmcia_get_first_tuple(handle, &tuple)) != CS_SUCCESS) {
+       if ((fail_rc = pcmcia_get_first_tuple(link, &tuple)) != CS_SUCCESS) {
                fail_fn = GetFirstTuple;
                goto cs_failed;
        }
-       if ((fail_rc = pcmcia_get_tuple_data(handle, &tuple)) != CS_SUCCESS) {
+       if ((fail_rc = pcmcia_get_tuple_data(link, &tuple)) != CS_SUCCESS) {
                fail_fn = GetTupleData;
                goto cs_failed;
        }
-       if ((fail_rc = pcmcia_parse_tuple(handle, &tuple, &parse))
+       if ((fail_rc = pcmcia_parse_tuple(link, &tuple, &parse))
                                                        != CS_SUCCESS) {
                fail_fn = ParseTuple;
                goto cs_failed;
        }
-       if ((fail_rc = pcmcia_get_configuration_info(handle, &conf))
-                                                       != CS_SUCCESS) {
-               fail_fn = GetConfigurationInfo;
-               goto cs_failed;
-       }
 
-       link->state |= DEV_CONFIG;
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
-       link->conf.Vcc = conf.Vcc;
 
        link->io.BasePort2 = 0;
        link->io.NumPorts2 = 0;
        link->io.Attributes2 = 0;
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       for (rc = pcmcia_get_first_tuple(handle, &tuple);
+       for (rc = pcmcia_get_first_tuple(link, &tuple);
             rc == CS_SUCCESS;
-            rc = pcmcia_get_next_tuple(handle, &tuple)) {
-               rc = pcmcia_get_tuple_data(handle, &tuple);
+            rc = pcmcia_get_next_tuple(link, &tuple)) {
+               rc = pcmcia_get_tuple_data(link, &tuple);
                if (rc != CS_SUCCESS)
                        continue;
-               rc = pcmcia_parse_tuple(handle, &tuple, &parse);
+               rc = pcmcia_parse_tuple(link, &tuple, &parse);
                if (rc != CS_SUCCESS)
                        continue;
 
@@ -585,13 +574,13 @@ static void reader_config(dev_link_t *link, int devno)
                        link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
                link->io.IOAddrLines = parse.cftable_entry.io.flags
                                                & CISTPL_IO_LINES_MASK;
-               rc = pcmcia_request_io(handle, &link->io);
+               rc = pcmcia_request_io(link, &link->io);
 
-               dev_printk(KERN_INFO, &handle_to_dev(handle), "foo");
+               dev_printk(KERN_INFO, &handle_to_dev(link), "foo");
                if (rc == CS_SUCCESS)
                        break;
                else
-                       dev_printk(KERN_INFO, &handle_to_dev(handle),
+                       dev_printk(KERN_INFO, &handle_to_dev(link),
                                   "pcmcia_request_io failed 0x%x\n", rc);
        }
        if (rc != CS_SUCCESS)
@@ -599,10 +588,10 @@ static void reader_config(dev_link_t *link, int devno)
 
        link->conf.IntType = 00000002;
 
-       if ((fail_rc = pcmcia_request_configuration(handle,&link->conf))
+       if ((fail_rc = pcmcia_request_configuration(link,&link->conf))
                                                                !=CS_SUCCESS) {
                fail_fn = RequestConfiguration;
-               dev_printk(KERN_INFO, &handle_to_dev(handle),
+               dev_printk(KERN_INFO, &handle_to_dev(link),
                           "pcmcia_request_configuration failed 0x%x\n",
                           fail_rc);
                goto cs_release;
@@ -612,57 +601,31 @@ static void reader_config(dev_link_t *link, int devno)
        sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno);
        dev->node.major = major;
        dev->node.minor = devno;
-       dev->node.next = NULL;
-       link->dev = &dev->node;
-       link->state &= ~DEV_CONFIG_PENDING;
+       dev->node.next = &dev->node;
 
        DEBUGP(2, dev, "device " DEVICE_NAME "%d at 0x%.4x-0x%.4x\n", devno,
              link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1);
        DEBUGP(2, dev, "<- reader_config (succ)\n");
 
-       return;
+       return 0;
 
 cs_failed:
-       cs_error(handle, fail_fn, fail_rc);
+       cs_error(link, fail_fn, fail_rc);
 cs_release:
        reader_release(link);
-       link->state &= ~DEV_CONFIG_PENDING;
-}
-
-static int reader_suspend(struct pcmcia_device *p_dev)
-{
-       dev_link_t *link = dev_to_instance(p_dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
+       return -ENODEV;
 }
 
-static int reader_resume(struct pcmcia_device *p_dev)
-{
-       dev_link_t *link = dev_to_instance(p_dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-       return 0;
-}
-
-static void reader_release(dev_link_t *link)
+static void reader_release(struct pcmcia_device *link)
 {
        cm4040_reader_release(link->priv);
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
+       pcmcia_disable_device(link);
 }
 
-static int reader_attach(struct pcmcia_device *p_dev)
+static int reader_probe(struct pcmcia_device *link)
 {
        struct reader_dev *dev;
-       dev_link_t *link;
-       int i;
+       int i, ret;
 
        for (i = 0; i < CM_MAX_DEV; i++) {
                if (dev_table[i] == NULL)
@@ -679,8 +642,8 @@ static int reader_attach(struct pcmcia_device *p_dev)
        dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
        dev->buffer_status = 0;
 
-       link = &dev->link;
        link->priv = dev;
+       dev->p_dev = link;
 
        link->conf.IntType = INT_MEMORY_AND_IO;
        dev_table[i] = link;
@@ -692,11 +655,9 @@ static int reader_attach(struct pcmcia_device *p_dev)
        init_timer(&dev->poll_timer);
        dev->poll_timer.function = &cm4040_do_poll;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       reader_config(link, i);
+       ret = reader_config(link, i);
+       if (ret)
+               return ret;
 
        class_device_create(cmx_class, NULL, MKDEV(major, i), NULL,
                            "cmx%d", i);
@@ -704,9 +665,8 @@ static int reader_attach(struct pcmcia_device *p_dev)
        return 0;
 }
 
-static void reader_detach(struct pcmcia_device *p_dev)
+static void reader_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct reader_dev *dev = link->priv;
        int devno;
 
@@ -718,10 +678,7 @@ static void reader_detach(struct pcmcia_device *p_dev)
        if (devno == CM_MAX_DEV)
                return;
 
-       link->state &= ~DEV_PRESENT;
-
-       if (link->state & DEV_CONFIG)
-               reader_release(link);
+       reader_release(link);
 
        dev_table[devno] = NULL;
        kfree(dev);
@@ -753,10 +710,8 @@ static struct pcmcia_driver reader_driver = {
        .drv            = {
                .name   = "cm4040_cs",
        },
-       .probe          = reader_attach,
+       .probe          = reader_probe,
        .remove         = reader_detach,
-       .suspend        = reader_suspend,
-       .resume         = reader_resume,
        .id_table       = cm4040_ids,
 };
 
index e6b714b..0721345 100644 (file)
@@ -228,7 +228,7 @@ typedef struct _mgslpc_info {
        struct  _input_signal_events    input_signal_events;
 
        /* PCMCIA support */
-       dev_link_t            link;
+       struct pcmcia_device    *p_dev;
        dev_node_t            node;
        int                   stop;
 
@@ -484,7 +484,7 @@ static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout);
 
 /* PCMCIA prototypes */
 
-static void mgslpc_config(dev_link_t *link);
+static int mgslpc_config(struct pcmcia_device *link);
 static void mgslpc_release(u_long arg);
 static void mgslpc_detach(struct pcmcia_device *p_dev);
 
@@ -533,14 +533,14 @@ static void ldisc_receive_buf(struct tty_struct *tty,
        }
 }
 
-static int mgslpc_attach(struct pcmcia_device *p_dev)
+static int mgslpc_probe(struct pcmcia_device *link)
 {
     MGSLPC_INFO *info;
-    dev_link_t *link;
-    
+    int ret;
+
     if (debug_level >= DEBUG_LEVEL_INFO)
            printk("mgslpc_attach\n");
-       
+
     info = (MGSLPC_INFO *)kmalloc(sizeof(MGSLPC_INFO), GFP_KERNEL);
     if (!info) {
            printk("Error can't allocate device instance data\n");
@@ -565,25 +565,22 @@ static int mgslpc_attach(struct pcmcia_device *p_dev)
     info->imrb_value = 0xffff;
     info->pim_value = 0xff;
 
-    link = &info->link;
+    info->p_dev = link;
     link->priv = info;
-    
-    /* Initialize the dev_link_t structure */
+
+    /* Initialize the struct pcmcia_device structure */
 
     /* Interrupt setup */
     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
     link->irq.IRQInfo1   = IRQ_LEVEL_ID;
     link->irq.Handler = NULL;
-    
+
     link->conf.Attributes = 0;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    mgslpc_config(link);
+    ret = mgslpc_config(link);
+    if (ret)
+           return ret;
 
     mgslpc_add_device(info);
 
@@ -596,15 +593,13 @@ static int mgslpc_attach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void mgslpc_config(dev_link_t *link)
+static int mgslpc_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     MGSLPC_INFO *info = link->priv;
     tuple_t tuple;
     cisparse_t parse;
     int last_fn, last_ret;
     u_char buf[64];
-    config_info_t conf;
     cistpl_cftable_entry_t dflt = { 0 };
     cistpl_cftable_entry_t *cfg;
     
@@ -617,27 +612,20 @@ static void mgslpc_config(dev_link_t *link)
     tuple.TupleData = buf;
     tuple.TupleDataMax = sizeof(buf);
     tuple.TupleOffset = 0;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present = parse.config.rmask[0];
-    
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
-    /* Look up the current Vcc */
-    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
-    link->conf.Vcc = conf.Vcc;
 
     /* get CIS configuration entry */
 
     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
 
     cfg = &(parse.cftable_entry);
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
 
     if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
     if (cfg->index == 0)
@@ -658,11 +646,10 @@ static void mgslpc_config(dev_link_t *link)
            link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
            link->io.BasePort1 = io->win[0].base;
            link->io.NumPorts1 = io->win[0].len;
-           CS_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io));
+           CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
     }
 
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
     link->conf.ConfigIndex = 8;
     link->conf.Present = PRESENT_OPTION;
@@ -670,9 +657,9 @@ static void mgslpc_config(dev_link_t *link)
     link->irq.Attributes |= IRQ_HANDLE_PRESENT;
     link->irq.Handler     = mgslpc_isr;
     link->irq.Instance    = info;
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
 
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
     info->io_base = link->io.BasePort1;
     info->irq_level = link->irq.AssignedIRQ;
@@ -680,7 +667,7 @@ static void mgslpc_config(dev_link_t *link)
     /* add to linked list of devices */
     sprintf(info->node.dev_name, "mgslpc0");
     info->node.major = info->node.minor = 0;
-    link->dev = &info->node;
+    link->dev_node = &info->node;
 
     printk(KERN_INFO "%s: index 0x%02x:",
           info->node.dev_name, link->conf.ConfigIndex);
@@ -690,13 +677,12 @@ static void mgslpc_config(dev_link_t *link)
            printk(", io 0x%04x-0x%04x", link->io.BasePort1,
                   link->io.BasePort1+link->io.NumPorts1-1);
     printk("\n");
-    
-    link->state &= ~DEV_CONFIG_PENDING;
-    return;
+    return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
     mgslpc_release((u_long)link);
+    return -ENODEV;
 }
 
 /* Card has been removed.
@@ -705,58 +691,38 @@ cs_failed:
  */
 static void mgslpc_release(u_long arg)
 {
-    dev_link_t *link = (dev_link_t *)arg;
+       struct pcmcia_device *link = (struct pcmcia_device *)arg;
 
-    if (debug_level >= DEBUG_LEVEL_INFO)
-           printk("mgslpc_release(0x%p)\n", link);
-
-    /* Unlink the device chain */
-    link->dev = NULL;
-    link->state &= ~DEV_CONFIG;
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("mgslpc_release(0x%p)\n", link);
 
-    pcmcia_release_configuration(link->handle);
-    if (link->io.NumPorts1)
-           pcmcia_release_io(link->handle, &link->io);
-    if (link->irq.AssignedIRQ)
-           pcmcia_release_irq(link->handle, &link->irq);
+       pcmcia_disable_device(link);
 }
 
-static void mgslpc_detach(struct pcmcia_device *p_dev)
+static void mgslpc_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-
-    if (debug_level >= DEBUG_LEVEL_INFO)
-           printk("mgslpc_detach(0x%p)\n", link);
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("mgslpc_detach(0x%p)\n", link);
 
-    if (link->state & DEV_CONFIG) {
-           ((MGSLPC_INFO *)link->priv)->stop = 1;
-           mgslpc_release((u_long)link);
-    }
+       ((MGSLPC_INFO *)link->priv)->stop = 1;
+       mgslpc_release((u_long)link);
 
-    mgslpc_remove_device((MGSLPC_INFO *)link->priv);
+       mgslpc_remove_device((MGSLPC_INFO *)link->priv);
 }
 
-static int mgslpc_suspend(struct pcmcia_device *dev)
+static int mgslpc_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        MGSLPC_INFO *info = link->priv;
 
-       link->state |= DEV_SUSPEND;
        info->stop = 1;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
 
        return 0;
 }
 
-static int mgslpc_resume(struct pcmcia_device *dev)
+static int mgslpc_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        MGSLPC_INFO *info = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_request_configuration(link->handle, &link->conf);
        info->stop = 0;
 
        return 0;
@@ -1280,7 +1246,7 @@ static irqreturn_t mgslpc_isr(int irq, void *dev_id, struct pt_regs * regs)
        if (!info)
                return IRQ_NONE;
                
-       if (!(info->link.state & DEV_CONFIG))
+       if (!(info->p_dev->_locked))
                return IRQ_HANDLED;
 
        spin_lock(&info->lock);
@@ -3033,7 +2999,7 @@ static struct pcmcia_driver mgslpc_driver = {
        .drv            = {
                .name   = "synclink_cs",
        },
-       .probe          = mgslpc_attach,
+       .probe          = mgslpc_probe,
        .remove         = mgslpc_detach,
        .id_table       = mgslpc_ids,
        .suspend        = mgslpc_suspend,
index 16e99db..d53f664 100644 (file)
@@ -60,6 +60,13 @@ config SOFT_WATCHDOG
 
 # ARM Architecture
 
+config AT91_WATCHDOG
+       tristate "AT91RM9200 watchdog"
+       depends on WATCHDOG && ARCH_AT91RM9200
+       help
+         Watchdog timer embedded into AT91RM9200 chips. This will reboot your
+         system when the timeout is reached.
+
 config 21285_WATCHDOG
        tristate "DC21285 watchdog"
        depends on WATCHDOG && FOOTBRIDGE
index d6f27fd..6ab77b6 100644 (file)
@@ -23,6 +23,7 @@ obj-$(CONFIG_WDTPCI) += wdt_pci.o
 obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 
 # ARM Architecture
+obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o
 obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
 obj-$(CONFIG_977_WATCHDOG) += wdt977.o
 obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o
diff --git a/drivers/char/watchdog/at91_wdt.c b/drivers/char/watchdog/at91_wdt.c
new file mode 100644 (file)
index 0000000..ac83bc4
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Watchdog driver for Atmel AT91RM9200 (Thunder)
+ *
+ *  Copyright (C) 2003 SAN People (Pty) Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
+
+#define WDT_DEFAULT_TIME       5       /* 5 seconds */
+#define WDT_MAX_TIME           256     /* 256 seconds */
+
+static int wdt_time = WDT_DEFAULT_TIME;
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+module_param(wdt_time, int, 0);
+MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+
+static unsigned long at91wdt_busy;
+
+/* ......................................................................... */
+
+/*
+ * Disable the watchdog.
+ */
+static void inline at91_wdt_stop(void)
+{
+       at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN);
+}
+
+/*
+ * Enable and reset the watchdog.
+ */
+static void inline at91_wdt_start(void)
+{
+       at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | (((65536 * wdt_time) >> 8) & AT91_ST_WDV));
+       at91_sys_write(AT91_ST_CR, AT91_ST_WDRST);
+}
+
+/*
+ * Reload the watchdog timer.  (ie, pat the watchdog)
+ */
+static void inline at91_wdt_reload(void)
+{
+       at91_sys_write(AT91_ST_CR, AT91_ST_WDRST);
+}
+
+/* ......................................................................... */
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int at91_wdt_open(struct inode *inode, struct file *file)
+{
+       if (test_and_set_bit(0, &at91wdt_busy))
+               return -EBUSY;
+
+       at91_wdt_start();
+       return nonseekable_open(inode, file);
+}
+
+/*
+ * Close the watchdog device.
+ * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
+ *  disabled.
+ */
+static int at91_wdt_close(struct inode *inode, struct file *file)
+{
+       if (!nowayout)
+               at91_wdt_stop();        /* Disable the watchdog when file is closed */
+
+       clear_bit(0, &at91wdt_busy);
+       return 0;
+}
+
+/*
+ * Change the watchdog time interval.
+ */
+static int at91_wdt_settimeout(int new_time)
+{
+       /*
+        * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz
+        *
+        * Since WDV is a 16-bit counter, the maximum period is
+        * 65536 / 0.256 = 256 seconds.
+        */
+       if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
+               return -EINVAL;
+
+       /* Set new watchdog time. It will be used when at91_wdt_start() is called. */
+       wdt_time = new_time;
+       return 0;
+}
+
+static struct watchdog_info at91_wdt_info = {
+       .identity       = "at91 watchdog",
+       .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+/*
+ * Handle commands from user-space.
+ */
+static int at91_wdt_ioctl(struct inode *inode, struct file *file,
+               unsigned int cmd, unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       int __user *p = argp;
+       int new_value;
+
+       switch(cmd) {
+               case WDIOC_KEEPALIVE:
+                       at91_wdt_reload();      /* pat the watchdog */
+                       return 0;
+
+               case WDIOC_GETSUPPORT:
+                       return copy_to_user(argp, &at91_wdt_info, sizeof(at91_wdt_info)) ? -EFAULT : 0;
+
+               case WDIOC_SETTIMEOUT:
+                       if (get_user(new_value, p))
+                               return -EFAULT;
+                               
+                       if (at91_wdt_settimeout(new_value))
+                               return -EINVAL;
+
+                       /* Enable new time value */
+                       at91_wdt_start();
+
+                       /* Return current value */
+                       return put_user(wdt_time, p);
+
+               case WDIOC_GETTIMEOUT:
+                       return put_user(wdt_time, p);
+
+               case WDIOC_GETSTATUS:
+               case WDIOC_GETBOOTSTATUS:
+                       return put_user(0, p);
+
+               case WDIOC_SETOPTIONS:
+                       if (get_user(new_value, p))
+                               return -EFAULT;
+
+                       if (new_value & WDIOS_DISABLECARD)
+                               at91_wdt_stop();
+                       if (new_value & WDIOS_ENABLECARD)
+                               at91_wdt_start();
+                       return 0;
+
+               default:
+                       return -ENOIOCTLCMD;
+       }
+}
+
+/*
+ * Pat the watchdog whenever device is written to.
+ */
+static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+       at91_wdt_reload();              /* pat the watchdog */
+       return len;
+}
+
+/* ......................................................................... */
+
+static struct file_operations at91wdt_fops = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .ioctl          = at91_wdt_ioctl,
+       .open           = at91_wdt_open,
+       .release        = at91_wdt_close,
+       .write          = at91_wdt_write,
+};
+
+static struct miscdevice at91wdt_miscdev = {
+       .minor          = WATCHDOG_MINOR,
+       .name           = "watchdog",
+       .fops           = &at91wdt_fops,
+};
+
+static int __init at91_wdt_init(void)
+{
+       int res;
+
+       /* Check that the heartbeat value is within range; if not reset to the default */
+       if (at91_wdt_settimeout(wdt_time)) {
+               at91_wdt_settimeout(WDT_DEFAULT_TIME);
+               printk(KERN_INFO "at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time);
+       }
+
+       res = misc_register(&at91wdt_miscdev);
+       if (res)
+               return res;
+
+       printk("AT91 Watchdog Timer enabled (%d seconds, nowayout=%d)\n", wdt_time, nowayout);
+       return 0;
+}
+
+static void __exit at91_wdt_exit(void)
+{
+       misc_deregister(&at91wdt_miscdev);
+}
+
+module_init(at91_wdt_init);
+module_exit(at91_wdt_exit);
+
+MODULE_AUTHOR("Andrew Victor");
+MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 8d6b249..6d44ca6 100644 (file)
 #include <linux/fs.h>          /* For file operations */
 #include <linux/ioport.h>      /* For io-port access */
 #include <linux/spinlock.h>    /* For spin_lock/spin_unlock/... */
-#include <linux/sched.h>       /* TASK_INTERRUPTIBLE, set_current_state() and friends */
-#include <linux/slab.h>                /* For kmalloc */
 
 #include <asm/uaccess.h>       /* For copy_to_user/put_user/... */
 #include <asm/io.h>            /* For inb/outb/... */
 
 /* Module and version information */
-#define WATCHDOG_VERSION "1.16"
-#define WATCHDOG_DATE "03 Jan 2006"
+#define WATCHDOG_VERSION "1.17"
+#define WATCHDOG_DATE "12 Feb 2006"
 #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
 #define WATCHDOG_NAME "pcwd"
 #define PFX WATCHDOG_NAME ": "
  * PCI-PC Watchdog card.
 */
 /* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */
-#define WD_WDRST                0x01   /* Previously reset state */
-#define WD_T110                 0x02   /* Temperature overheat sense */
-#define WD_HRTBT                0x04   /* Heartbeat sense */
-#define WD_RLY2                 0x08   /* External relay triggered */
-#define WD_SRLY2                0x80   /* Software external relay triggered */
+#define WD_WDRST               0x01    /* Previously reset state */
+#define WD_T110                        0x02    /* Temperature overheat sense */
+#define WD_HRTBT               0x04    /* Heartbeat sense */
+#define WD_RLY2                        0x08    /* External relay triggered */
+#define WD_SRLY2               0x80    /* Software external relay triggered */
 /* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */
-#define WD_REVC_WTRP            0x01   /* Watchdog Trip status */
-#define WD_REVC_HRBT            0x02   /* Watchdog Heartbeat */
-#define WD_REVC_TTRP            0x04   /* Temperature Trip status */
+#define WD_REVC_WTRP           0x01    /* Watchdog Trip status */
+#define WD_REVC_HRBT           0x02    /* Watchdog Heartbeat */
+#define WD_REVC_TTRP           0x04    /* Temperature Trip status */
+#define WD_REVC_RL2A           0x08    /* Relay 2 activated by on-board processor */
+#define WD_REVC_RL1A           0x10    /* Relay 1 active */
+#define WD_REVC_R2DS           0x40    /* Relay 2 disable */
+#define WD_REVC_RLY2           0x80    /* Relay 2 activated? */
 /* Port 2 : Control Status #2 */
 #define WD_WDIS                        0x10    /* Watchdog Disabled */
 #define WD_ENTP                        0x20    /* Watchdog Enable Temperature Trip */
 #define CMD_ISA_VERSION_HUNDRETH       0x03
 #define CMD_ISA_VERSION_MINOR          0x04
 #define CMD_ISA_SWITCH_SETTINGS                0x05
+#define CMD_ISA_RESET_PC               0x06
+#define CMD_ISA_ARM_0                  0x07
+#define CMD_ISA_ARM_30                 0x08
+#define CMD_ISA_ARM_60                 0x09
 #define CMD_ISA_DELAY_TIME_2SECS       0x0A
 #define CMD_ISA_DELAY_TIME_4SECS       0x0B
 #define CMD_ISA_DELAY_TIME_8SECS       0x0C
+#define CMD_ISA_RESET_RELAYS           0x0D
 
 /*
  * We are using an kernel timer to do the pinging of the watchdog
@@ -142,6 +149,7 @@ static atomic_t open_allowed = ATOMIC_INIT(1);
 static char expect_close;
 static int temp_panic;
 static struct {                                /* this is private data for each ISA-PC watchdog card */
+       char fw_ver_str[6];             /* The cards firmware version */
        int revision;                   /* The card's revision */
        int supports_temp;              /* Wether or not the card has a temperature device */
        int command_mode;               /* Wether or not the card is in command mode */
@@ -153,6 +161,13 @@ static struct {                            /* this is private data for each ISA-PC watchdog card */
 } pcwd_private;
 
 /* module parameters */
+#define QUIET  0       /* Default */
+#define VERBOSE        1       /* Verbose */
+#define DEBUG  2       /* print fancy stuff too */
+static int debug = QUIET;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)");
+
 #define WATCHDOG_HEARTBEAT 60          /* 60 sec default heartbeat */
 static int heartbeat = WATCHDOG_HEARTBEAT;
 module_param(heartbeat, int, 0);
@@ -172,6 +187,10 @@ static int send_isa_command(int cmd)
        int control_status;
        int port0, last_port0;  /* Double read for stabilising */
 
+       if (debug >= DEBUG)
+               printk(KERN_DEBUG PFX "sending following data cmd=0x%02x\n",
+                       cmd);
+
        /* The WCMD bit must be 1 and the command is only 4 bits in size */
        control_status = (cmd & 0x0F) | WD_WCMD;
        outb_p(control_status, pcwd_private.io_addr + 2);
@@ -188,6 +207,10 @@ static int send_isa_command(int cmd)
                udelay (250);
        }
 
+       if (debug >= DEBUG)
+               printk(KERN_DEBUG PFX "received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n",
+                       cmd, port0, last_port0);
+
        return port0;
 }
 
@@ -214,6 +237,10 @@ static int set_command_mode(void)
        spin_unlock(&pcwd_private.io_lock);
        pcwd_private.command_mode = found;
 
+       if (debug >= DEBUG)
+               printk(KERN_DEBUG PFX "command_mode=%d\n",
+                               pcwd_private.command_mode);
+
        return(found);
 }
 
@@ -226,6 +253,10 @@ static void unset_command_mode(void)
        spin_unlock(&pcwd_private.io_lock);
 
        pcwd_private.command_mode = 0;
+
+       if (debug >= DEBUG)
+               printk(KERN_DEBUG PFX "command_mode=%d\n",
+                               pcwd_private.command_mode);
 }
 
 static inline void pcwd_check_temperature_support(void)
@@ -234,27 +265,22 @@ static inline void pcwd_check_temperature_support(void)
                pcwd_private.supports_temp = 1;
 }
 
-static inline char *get_firmware(void)
+static inline void pcwd_get_firmware(void)
 {
        int one, ten, hund, minor;
-       char *ret;
 
-       ret = kmalloc(6, GFP_KERNEL);
-       if(ret == NULL)
-               return NULL;
+       strcpy(pcwd_private.fw_ver_str, "ERROR");
 
        if (set_command_mode()) {
                one = send_isa_command(CMD_ISA_VERSION_INTEGER);
                ten = send_isa_command(CMD_ISA_VERSION_TENTH);
                hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH);
                minor = send_isa_command(CMD_ISA_VERSION_MINOR);
-               sprintf(ret, "%c.%c%c%c", one, ten, hund, minor);
+               sprintf(pcwd_private.fw_ver_str, "%c.%c%c%c", one, ten, hund, minor);
        }
-       else
-               sprintf(ret, "ERROR");
-
        unset_command_mode();
-       return(ret);
+
+       return;
 }
 
 static inline int pcwd_get_option_switches(void)
@@ -272,17 +298,15 @@ static inline int pcwd_get_option_switches(void)
 
 static void pcwd_show_card_info(void)
 {
-       char *firmware;
        int option_switches;
 
        /* Get some extra info from the hardware (in command/debug/diag mode) */
        if (pcwd_private.revision == PCWD_REVISION_A)
                printk(KERN_INFO PFX "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n", pcwd_private.io_addr);
        else if (pcwd_private.revision == PCWD_REVISION_C) {
-               firmware = get_firmware();
+               pcwd_get_firmware();
                printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n",
-                       pcwd_private.io_addr, firmware);
-               kfree(firmware);
+                       pcwd_private.io_addr, pcwd_private.fw_ver_str);
                option_switches = pcwd_get_option_switches();
                printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
                        option_switches,
@@ -362,6 +386,10 @@ static int pcwd_start(void)
                        return -EIO;
                }
        }
+
+       if (debug >= VERBOSE)
+               printk(KERN_DEBUG PFX "Watchdog started\n");
+
        return 0;
 }
 
@@ -386,6 +414,10 @@ static int pcwd_stop(void)
                        return -EIO;
                }
        }
+
+       if (debug >= VERBOSE)
+               printk(KERN_DEBUG PFX "Watchdog stopped\n");
+
        return 0;
 }
 
@@ -393,6 +425,10 @@ static int pcwd_keepalive(void)
 {
        /* user land ping */
        pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
+
+       if (debug >= DEBUG)
+               printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n");
+
        return 0;
 }
 
@@ -402,12 +438,17 @@ static int pcwd_set_heartbeat(int t)
                return -EINVAL;
 
        heartbeat = t;
+
+       if (debug >= VERBOSE)
+               printk(KERN_DEBUG PFX "New heartbeat: %d\n",
+                      heartbeat);
+
        return 0;
 }
 
 static int pcwd_get_status(int *status)
 {
-       int card_status;
+       int control_status;
 
        *status=0;
        spin_lock(&pcwd_private.io_lock);
@@ -415,37 +456,39 @@ static int pcwd_get_status(int *status)
                /* Rev A cards return status information from
                 * the base register, which is used for the
                 * temperature in other cards. */
-               card_status = inb(pcwd_private.io_addr);
+               control_status = inb(pcwd_private.io_addr);
        else {
                /* Rev C cards return card status in the base
                 * address + 1 register. And use different bits
                 * to indicate a card initiated reset, and an
                 * over-temperature condition. And the reboot
                 * status can be reset. */
-               card_status = inb(pcwd_private.io_addr + 1);
+               control_status = inb(pcwd_private.io_addr + 1);
        }
        spin_unlock(&pcwd_private.io_lock);
 
        if (pcwd_private.revision == PCWD_REVISION_A) {
-               if (card_status & WD_WDRST)
+               if (control_status & WD_WDRST)
                        *status |= WDIOF_CARDRESET;
 
-               if (card_status & WD_T110) {
+               if (control_status & WD_T110) {
                        *status |= WDIOF_OVERHEAT;
                        if (temp_panic) {
                                printk (KERN_INFO PFX "Temperature overheat trip!\n");
                                kernel_power_off();
+                               /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */
                        }
                }
        } else {
-               if (card_status & WD_REVC_WTRP)
+               if (control_status & WD_REVC_WTRP)
                        *status |= WDIOF_CARDRESET;
 
-               if (card_status & WD_REVC_TTRP) {
+               if (control_status & WD_REVC_TTRP) {
                        *status |= WDIOF_OVERHEAT;
                        if (temp_panic) {
                                printk (KERN_INFO PFX "Temperature overheat trip!\n");
                                kernel_power_off();
+                               /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */
                        }
                }
        }
@@ -455,9 +498,25 @@ static int pcwd_get_status(int *status)
 
 static int pcwd_clear_status(void)
 {
+       int control_status;
+
        if (pcwd_private.revision == PCWD_REVISION_C) {
                spin_lock(&pcwd_private.io_lock);
-               outb_p(0x00, pcwd_private.io_addr + 1); /* clear reset status */
+
+               if (debug >= VERBOSE)
+                       printk(KERN_INFO PFX "clearing watchdog trip status\n");
+
+               control_status = inb_p(pcwd_private.io_addr + 1);
+
+               if (debug >= DEBUG) {
+                       printk(KERN_DEBUG PFX "status was: 0x%02x\n", control_status);
+                       printk(KERN_DEBUG PFX "sending: 0x%02x\n",
+                               (control_status & WD_REVC_R2DS));
+               }
+
+               /* clear reset status & Keep Relay 2 disable state as it is */
+               outb_p((control_status & WD_REVC_R2DS), pcwd_private.io_addr + 1);
+
                spin_unlock(&pcwd_private.io_lock);
        }
        return 0;
@@ -481,6 +540,11 @@ static int pcwd_get_temperature(int *temperature)
        *temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32;
        spin_unlock(&pcwd_private.io_lock);
 
+       if (debug >= DEBUG) {
+               printk(KERN_DEBUG PFX "temperature is: %d F\n",
+                       *temperature);
+       }
+
        return 0;
 }
 
@@ -599,6 +663,8 @@ static ssize_t pcwd_write(struct file *file, const char __user *buf, size_t len,
 static int pcwd_open(struct inode *inode, struct file *file)
 {
        if (!atomic_dec_and_test(&open_allowed) ) {
+               if (debug >= VERBOSE)
+                       printk(KERN_ERR PFX "Attempt to open already opened device.\n");
                atomic_inc( &open_allowed );
                return -EBUSY;
        }
@@ -922,7 +988,8 @@ static void __exit pcwd_cleanup_module(void)
 {
        if (pcwd_private.io_addr)
                pcwatchdog_exit();
-       return;
+
+       printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
 }
 
 module_init(pcwd_init_module);
index 2700c5c..3fdfda9 100644 (file)
@@ -705,7 +705,8 @@ err_out_misc_deregister:
 err_out_unregister_reboot:
        unregister_reboot_notifier(&usb_pcwd_notifier);
 error:
-       usb_pcwd_delete (usb_pcwd);
+       if (usb_pcwd)
+               usb_pcwd_delete(usb_pcwd);
        usb_pcwd_device = NULL;
        return retval;
 }
index 7636c1a..23a9e1e 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/module.h>
 #include <linux/timer.h>
 #include <linux/dmi.h>
-#include <linux/mutex.h>
 #include <asm/io.h>
 
 #define HDAPS_LOW_PORT         0x1600  /* first port used by hdaps */
@@ -71,10 +70,10 @@ static u8 km_activity;
 static int rest_x;
 static int rest_y;
 
-static DEFINE_MUTEX(hdaps_mutex);
+static DECLARE_MUTEX(hdaps_sem);
 
 /*
- * __get_latch - Get the value from a given port.  Callers must hold hdaps_mutex.
+ * __get_latch - Get the value from a given port.  Callers must hold hdaps_sem.
  */
 static inline u8 __get_latch(u16 port)
 {
@@ -83,7 +82,7 @@ static inline u8 __get_latch(u16 port)
 
 /*
  * __check_latch - Check a port latch for a given value.  Returns zero if the
- * port contains the given value.  Callers must hold hdaps_mutex.
+ * port contains the given value.  Callers must hold hdaps_sem.
  */
 static inline int __check_latch(u16 port, u8 val)
 {
@@ -94,7 +93,7 @@ static inline int __check_latch(u16 port, u8 val)
 
 /*
  * __wait_latch - Wait up to 100us for a port latch to get a certain value,
- * returning zero if the value is obtained.  Callers must hold hdaps_mutex.
+ * returning zero if the value is obtained.  Callers must hold hdaps_sem.
  */
 static int __wait_latch(u16 port, u8 val)
 {
@@ -111,7 +110,7 @@ static int __wait_latch(u16 port, u8 val)
 
 /*
  * __device_refresh - request a refresh from the accelerometer.  Does not wait
- * for refresh to complete.  Callers must hold hdaps_mutex.
+ * for refresh to complete.  Callers must hold hdaps_sem.
  */
 static void __device_refresh(void)
 {
@@ -125,7 +124,7 @@ static void __device_refresh(void)
 /*
  * __device_refresh_sync - request a synchronous refresh from the
  * accelerometer.  We wait for the refresh to complete.  Returns zero if
- * successful and nonzero on error.  Callers must hold hdaps_mutex.
+ * successful and nonzero on error.  Callers must hold hdaps_sem.
  */
 static int __device_refresh_sync(void)
 {
@@ -135,7 +134,7 @@ static int __device_refresh_sync(void)
 
 /*
  * __device_complete - indicate to the accelerometer that we are done reading
- * data, and then initiate an async refresh.  Callers must hold hdaps_mutex.
+ * data, and then initiate an async refresh.  Callers must hold hdaps_sem.
  */
 static inline void __device_complete(void)
 {
@@ -153,7 +152,7 @@ static int hdaps_readb_one(unsigned int port, u8 *val)
 {
        int ret;
 
-       mutex_lock(&hdaps_mutex);
+       down(&hdaps_sem);
 
        /* do a sync refresh -- we need to be sure that we read fresh data */
        ret = __device_refresh_sync();
@@ -164,7 +163,7 @@ static int hdaps_readb_one(unsigned int port, u8 *val)
        __device_complete();
 
 out:
-       mutex_unlock(&hdaps_mutex);
+       up(&hdaps_sem);
        return ret;
 }
 
@@ -199,9 +198,9 @@ static int hdaps_read_pair(unsigned int port1, unsigned int port2,
 {
        int ret;
 
-       mutex_lock(&hdaps_mutex);
+       down(&hdaps_sem);
        ret = __hdaps_read_pair(port1, port2, val1, val2);
-       mutex_unlock(&hdaps_mutex);
+       up(&hdaps_sem);
 
        return ret;
 }
@@ -214,7 +213,7 @@ static int hdaps_device_init(void)
 {
        int total, ret = -ENXIO;
 
-       mutex_lock(&hdaps_mutex);
+       down(&hdaps_sem);
 
        outb(0x13, 0x1610);
        outb(0x01, 0x161f);
@@ -280,7 +279,7 @@ static int hdaps_device_init(void)
        }
 
 out:
-       mutex_unlock(&hdaps_mutex);
+       up(&hdaps_sem);
        return ret;
 }
 
@@ -314,7 +313,7 @@ static struct platform_driver hdaps_driver = {
 };
 
 /*
- * hdaps_calibrate - Set our "resting" values.  Callers must hold hdaps_mutex.
+ * hdaps_calibrate - Set our "resting" values.  Callers must hold hdaps_sem.
  */
 static void hdaps_calibrate(void)
 {
@@ -326,7 +325,7 @@ static void hdaps_mousedev_poll(unsigned long unused)
        int x, y;
 
        /* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
-       if (!mutex_trylock(&hdaps_mutex)) {
+       if (down_trylock(&hdaps_sem)) {
                mod_timer(&hdaps_timer,jiffies + HDAPS_POLL_PERIOD);
                return;
        }
@@ -341,7 +340,7 @@ static void hdaps_mousedev_poll(unsigned long unused)
        mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD);
 
 out:
-       mutex_unlock(&hdaps_mutex);
+       up(&hdaps_sem);
 }
 
 
@@ -421,9 +420,9 @@ static ssize_t hdaps_calibrate_store(struct device *dev,
                                     struct device_attribute *attr,
                                     const char *buf, size_t count)
 {
-       mutex_lock(&hdaps_mutex);
+       down(&hdaps_sem);
        hdaps_calibrate();
-       mutex_unlock(&hdaps_mutex);
+       up(&hdaps_sem);
 
        return count;
 }
index 6213bd3..4961f1e 100644 (file)
@@ -81,14 +81,14 @@ static const char ide_major[] = {
 };
 
 typedef struct ide_info_t {
-    dev_link_t link;
+       struct pcmcia_device    *p_dev;
     int                ndev;
     dev_node_t node;
     int                hd;
 } ide_info_t;
 
-static void ide_release(dev_link_t *);
-static void ide_config(dev_link_t *);
+static void ide_release(struct pcmcia_device *);
+static int ide_config(struct pcmcia_device *);
 
 static void ide_detach(struct pcmcia_device *p_dev);
 
@@ -103,10 +103,9 @@ static void ide_detach(struct pcmcia_device *p_dev);
 
 ======================================================================*/
 
-static int ide_attach(struct pcmcia_device *p_dev)
+static int ide_probe(struct pcmcia_device *link)
 {
     ide_info_t *info;
-    dev_link_t *link;
 
     DEBUG(0, "ide_attach()\n");
 
@@ -114,7 +113,9 @@ static int ide_attach(struct pcmcia_device *p_dev)
     info = kzalloc(sizeof(*info), GFP_KERNEL);
     if (!info)
        return -ENOMEM;
-    link = &info->link; link->priv = info;
+
+    info->p_dev = link;
+    link->priv = info;
 
     link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
     link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
@@ -122,16 +123,9 @@ static int ide_attach(struct pcmcia_device *p_dev)
     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
     link->irq.IRQInfo1 = IRQ_LEVEL_ID;
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    ide_config(link);
-
-    return 0;
+    return ide_config(link);
 } /* ide_attach */
 
 /*======================================================================
@@ -143,14 +137,11 @@ static int ide_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void ide_detach(struct pcmcia_device *p_dev)
+static void ide_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-
     DEBUG(0, "ide_detach(0x%p)\n", link);
 
-    if (link->state & DEV_CONFIG)
-       ide_release(link);
+    ide_release(link);
 
     kfree(link->priv);
 } /* ide_detach */
@@ -177,9 +168,8 @@ static int idecs_register(unsigned long io, unsigned long ctl, unsigned long irq
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void ide_config(dev_link_t *link)
+static int ide_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     ide_info_t *info = link->priv;
     tuple_t tuple;
     struct {
@@ -203,34 +193,30 @@ static void ide_config(dev_link_t *link)
     tuple.TupleDataMax = 255;
     tuple.Attributes = 0;
     tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &stk->parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &stk->parse));
     link->conf.ConfigBase = stk->parse.config.base;
     link->conf.Present = stk->parse.config.rmask[0];
 
     tuple.DesiredTuple = CISTPL_MANFID;
-    if (!pcmcia_get_first_tuple(handle, &tuple) &&
-       !pcmcia_get_tuple_data(handle, &tuple) &&
-       !pcmcia_parse_tuple(handle, &tuple, &stk->parse))
+    if (!pcmcia_get_first_tuple(link, &tuple) &&
+       !pcmcia_get_tuple_data(link, &tuple) &&
+       !pcmcia_parse_tuple(link, &tuple, &stk->parse))
        is_kme = ((stk->parse.manfid.manf == MANFID_KME) &&
                  ((stk->parse.manfid.card == PRODID_KME_KXLC005_A) ||
                   (stk->parse.manfid.card == PRODID_KME_KXLC005_B)));
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
     /* Not sure if this is right... look up the current Vcc */
-    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &stk->conf));
-    link->conf.Vcc = stk->conf.Vcc;
+    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &stk->conf));
 
     pass = io_base = ctl_base = 0;
     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
     tuple.Attributes = 0;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     while (1) {
-       if (pcmcia_get_tuple_data(handle, &tuple) != 0) goto next_entry;
-       if (pcmcia_parse_tuple(handle, &tuple, &stk->parse) != 0) goto next_entry;
+       if (pcmcia_get_tuple_data(link, &tuple) != 0) goto next_entry;
+       if (pcmcia_parse_tuple(link, &tuple, &stk->parse) != 0) goto next_entry;
 
        /* Check for matching Vcc, unless we're desperate */
        if (!pass) {
@@ -244,10 +230,10 @@ static void ide_config(dev_link_t *link)
        }
 
        if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
-           link->conf.Vpp1 = link->conf.Vpp2 =
+           link->conf.Vpp =
                cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
        else if (stk->dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
-           link->conf.Vpp1 = link->conf.Vpp2 =
+           link->conf.Vpp =
                stk->dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
 
        if ((cfg->io.nwin > 0) || (stk->dflt.io.nwin > 0)) {
@@ -261,14 +247,14 @@ static void ide_config(dev_link_t *link)
                link->io.NumPorts1 = 8;
                link->io.BasePort2 = io->win[1].base;
                link->io.NumPorts2 = (is_kme) ? 2 : 1;
-               if (pcmcia_request_io(link->handle, &link->io) != 0)
+               if (pcmcia_request_io(link, &link->io) != 0)
                        goto next_entry;
                io_base = link->io.BasePort1;
                ctl_base = link->io.BasePort2;
            } else if ((io->nwin == 1) && (io->win[0].len >= 16)) {
                link->io.NumPorts1 = io->win[0].len;
                link->io.NumPorts2 = 0;
-               if (pcmcia_request_io(link->handle, &link->io) != 0)
+               if (pcmcia_request_io(link, &link->io) != 0)
                        goto next_entry;
                io_base = link->io.BasePort1;
                ctl_base = link->io.BasePort1 + 0x0e;
@@ -281,16 +267,16 @@ static void ide_config(dev_link_t *link)
        if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
            memcpy(&stk->dflt, cfg, sizeof(stk->dflt));
        if (pass) {
-           CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
-       } else if (pcmcia_get_next_tuple(handle, &tuple) != 0) {
-           CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+           CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
+       } else if (pcmcia_get_next_tuple(link, &tuple) != 0) {
+           CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
            memset(&stk->dflt, 0, sizeof(stk->dflt));
            pass++;
        }
     }
 
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
     /* disable drive interrupts during IDE probe */
     outb(0x02, ctl_base);
@@ -301,12 +287,12 @@ static void ide_config(dev_link_t *link)
 
     /* retry registration in case device is still spinning up */
     for (hd = -1, i = 0; i < 10; i++) {
-       hd = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, handle);
+       hd = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link);
        if (hd >= 0) break;
        if (link->io.NumPorts1 == 0x20) {
            outb(0x02, ctl_base + 0x10);
            hd = idecs_register(io_base + 0x10, ctl_base + 0x10,
-                               link->irq.AssignedIRQ, handle);
+                               link->irq.AssignedIRQ, link);
            if (hd >= 0) {
                io_base += 0x10;
                ctl_base += 0x10;
@@ -328,25 +314,23 @@ static void ide_config(dev_link_t *link)
     info->node.major = ide_major[hd];
     info->node.minor = 0;
     info->hd = hd;
-    link->dev = &info->node;
-    printk(KERN_INFO "ide-cs: %s: Vcc = %d.%d, Vpp = %d.%d\n",
-          info->node.dev_name, link->conf.Vcc / 10, link->conf.Vcc % 10,
-          link->conf.Vpp1 / 10, link->conf.Vpp1 % 10);
+    link->dev_node = &info->node;
+    printk(KERN_INFO "ide-cs: %s: Vpp = %d.%d\n",
+          info->node.dev_name, link->conf.Vpp / 10, link->conf.Vpp % 10);
 
-    link->state &= ~DEV_CONFIG_PENDING;
     kfree(stk);
-    return;
+    return 0;
 
 err_mem:
     printk(KERN_NOTICE "ide-cs: ide_config failed memory allocation\n");
     goto failed;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 failed:
     kfree(stk);
     ide_release(link);
-    link->state &= ~DEV_CONFIG_PENDING;
+    return -ENODEV;
 } /* ide_config */
 
 /*======================================================================
@@ -357,7 +341,7 @@ failed:
     
 ======================================================================*/
 
-void ide_release(dev_link_t *link)
+void ide_release(struct pcmcia_device *link)
 {
     ide_info_t *info = link->priv;
     
@@ -369,37 +353,10 @@ void ide_release(dev_link_t *link)
        ide_unregister(info->hd);
     }
     info->ndev = 0;
-    link->dev = NULL;
-    
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    
-    link->state &= ~DEV_CONFIG;
 
+    pcmcia_disable_device(link);
 } /* ide_release */
 
-static int ide_suspend(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int ide_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (DEV_OK(link))
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-       return 0;
-}
 
 /*======================================================================
 
@@ -459,11 +416,9 @@ static struct pcmcia_driver ide_cs_driver = {
        .drv            = {
                .name   = "ide-cs",
        },
-       .probe          = ide_attach,
+       .probe          = ide_probe,
        .remove         = ide_detach,
        .id_table       = ide_ids,
-       .suspend        = ide_suspend,
-       .resume         = ide_resume,
 };
 
 static int __init init_ide_cs(void)
index 2c765ca..f420660 100644 (file)
@@ -496,22 +496,17 @@ static struct sbp2_command_info *sbp2util_find_command_for_orb(
 /*
  * This function finds the sbp2_command for a given outstanding SCpnt.
  * Only looks at the inuse list.
+ * Must be called with scsi_id->sbp2_command_orb_lock held.
  */
-static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(struct scsi_id_instance_data *scsi_id, void *SCpnt)
+static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(
+               struct scsi_id_instance_data *scsi_id, void *SCpnt)
 {
        struct sbp2_command_info *command;
-       unsigned long flags;
 
-       spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
-       if (!list_empty(&scsi_id->sbp2_command_orb_inuse)) {
-               list_for_each_entry(command, &scsi_id->sbp2_command_orb_inuse, list) {
-                       if (command->Current_SCpnt == SCpnt) {
-                               spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
+       if (!list_empty(&scsi_id->sbp2_command_orb_inuse))
+               list_for_each_entry(command, &scsi_id->sbp2_command_orb_inuse, list)
+                       if (command->Current_SCpnt == SCpnt)
                                return command;
-                       }
-               }
-       }
-       spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
        return NULL;
 }
 
@@ -580,17 +575,15 @@ static void sbp2util_free_command_dma(struct sbp2_command_info *command)
 
 /*
  * This function moves a command to the completed orb list.
+ * Must be called with scsi_id->sbp2_command_orb_lock held.
  */
-static void sbp2util_mark_command_completed(struct scsi_id_instance_data *scsi_id,
-                                           struct sbp2_command_info *command)
+static void sbp2util_mark_command_completed(
+               struct scsi_id_instance_data *scsi_id,
+               struct sbp2_command_info *command)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
        list_del(&command->list);
        sbp2util_free_command_dma(command);
        list_add_tail(&command->list, &scsi_id->sbp2_command_orb_completed);
-       spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 }
 
 /*
@@ -2148,7 +2141,9 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
                 * Matched status with command, now grab scsi command pointers and check status
                 */
                SCpnt = command->Current_SCpnt;
+               spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
                sbp2util_mark_command_completed(scsi_id, command);
+               spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 
                if (SCpnt) {
 
@@ -2484,6 +2479,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
                (struct scsi_id_instance_data *)SCpnt->device->host->hostdata[0];
        struct sbp2scsi_host_info *hi = scsi_id->hi;
        struct sbp2_command_info *command;
+       unsigned long flags;
 
        SBP2_ERR("aborting sbp2 command");
        scsi_print_command(SCpnt);
@@ -2494,6 +2490,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
                 * Right now, just return any matching command structures
                 * to the free pool.
                 */
+               spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
                command = sbp2util_find_command_for_SCpnt(scsi_id, SCpnt);
                if (command) {
                        SBP2_DEBUG("Found command to abort");
@@ -2511,6 +2508,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
                                command->Current_done(command->Current_SCpnt);
                        }
                }
+               spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 
                /*
                 * Initiate a fetch agent reset.
index bdf0891..afc612b 100644 (file)
@@ -30,6 +30,7 @@ config INFINIBAND_USER_ACCESS
          <http://www.openib.org>.
 
 source "drivers/infiniband/hw/mthca/Kconfig"
+source "drivers/infiniband/hw/ipath/Kconfig"
 
 source "drivers/infiniband/ulp/ipoib/Kconfig"
 
index a43fb34..eea2732 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_INFINIBAND)               += core/
 obj-$(CONFIG_INFINIBAND_MTHCA)         += hw/mthca/
+obj-$(CONFIG_IPATH_CORE)               += hw/ipath/
 obj-$(CONFIG_INFINIBAND_IPOIB)         += ulp/ipoib/
 obj-$(CONFIG_INFINIBAND_SRP)           += ulp/srp/
diff --git a/drivers/infiniband/hw/ipath/Kconfig b/drivers/infiniband/hw/ipath/Kconfig
new file mode 100644 (file)
index 0000000..9ea67c4
--- /dev/null
@@ -0,0 +1,16 @@
+config IPATH_CORE
+       tristate "PathScale InfiniPath Driver"
+       depends on 64BIT && PCI_MSI && NET
+       ---help---
+       This is a low-level driver for PathScale InfiniPath host channel
+       adapters (HCAs) based on the HT-400 and PE-800 chips.
+
+config INFINIBAND_IPATH
+       tristate "PathScale InfiniPath Verbs Driver"
+       depends on IPATH_CORE && INFINIBAND
+       ---help---
+       This is a driver that provides InfiniBand verbs support for
+       PathScale InfiniPath host channel adapters (HCAs).  This
+       allows these devices to be used with both kernel upper level
+       protocols such as IP-over-InfiniBand as well as with userspace
+       applications (in conjunction with InfiniBand userspace access).
diff --git a/drivers/infiniband/hw/ipath/Makefile b/drivers/infiniband/hw/ipath/Makefile
new file mode 100644 (file)
index 0000000..b4d084a
--- /dev/null
@@ -0,0 +1,36 @@
+EXTRA_CFLAGS += -DIPATH_IDSTR='"PathScale kernel.org driver"' \
+       -DIPATH_KERN_TYPE=0
+
+obj-$(CONFIG_IPATH_CORE) += ipath_core.o
+obj-$(CONFIG_INFINIBAND_IPATH) += ib_ipath.o
+
+ipath_core-y := \
+       ipath_diag.o \
+       ipath_driver.o \
+       ipath_eeprom.o \
+       ipath_file_ops.o \
+       ipath_fs.o \
+       ipath_ht400.o \
+       ipath_init_chip.o \
+       ipath_intr.o \
+       ipath_layer.o \
+       ipath_pe800.o \
+       ipath_stats.o \
+       ipath_sysfs.o \
+       ipath_user_pages.o
+
+ipath_core-$(CONFIG_X86_64) += ipath_wc_x86_64.o
+
+ib_ipath-y := \
+       ipath_cq.o \
+       ipath_keys.o \
+       ipath_mad.o \
+       ipath_mr.o \
+       ipath_qp.o \
+       ipath_rc.o \
+       ipath_ruc.o \
+       ipath_srq.o \
+       ipath_uc.o \
+       ipath_ud.o \
+       ipath_verbs.o \
+       ipath_verbs_mcast.o
diff --git a/drivers/infiniband/hw/ipath/ipath_common.h b/drivers/infiniband/hw/ipath/ipath_common.h
new file mode 100644 (file)
index 0000000..48a5524
--- /dev/null
@@ -0,0 +1,616 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _IPATH_COMMON_H
+#define _IPATH_COMMON_H
+
+/*
+ * This file contains defines, structures, etc. that are used
+ * to communicate between kernel and user code.
+ */
+
+/* This is the IEEE-assigned OUI for PathScale, Inc. */
+#define IPATH_SRC_OUI_1 0x00
+#define IPATH_SRC_OUI_2 0x11
+#define IPATH_SRC_OUI_3 0x75
+
+/* version of protocol header (known to chip also). In the long run,
+ * we should be able to generate and accept a range of version numbers;
+ * for now we only accept one, and it's compiled in.
+ */
+#define IPS_PROTO_VERSION 2
+
+/*
+ * These are compile time constants that you may want to enable or disable
+ * if you are trying to debug problems with code or performance.
+ * IPATH_VERBOSE_TRACING define as 1 if you want additional tracing in
+ * fastpath code
+ * IPATH_TRACE_REGWRITES define as 1 if you want register writes to be
+ * traced in faspath code
+ * _IPATH_TRACING define as 0 if you want to remove all tracing in a
+ * compilation unit
+ * _IPATH_DEBUGGING define as 0 if you want to remove debug prints
+ */
+
+/*
+ * The value in the BTH QP field that InfiniPath uses to differentiate
+ * an infinipath protocol IB packet vs standard IB transport
+ */
+#define IPATH_KD_QP 0x656b79
+
+/*
+ * valid states passed to ipath_set_linkstate() user call
+ */
+#define IPATH_IB_LINKDOWN              0
+#define IPATH_IB_LINKARM               1
+#define IPATH_IB_LINKACTIVE            2
+#define IPATH_IB_LINKINIT              3
+#define IPATH_IB_LINKDOWN_SLEEP                4
+#define IPATH_IB_LINKDOWN_DISABLE      5
+
+/*
+ * stats maintained by the driver.  For now, at least, this is global
+ * to all minor devices.
+ */
+struct infinipath_stats {
+       /* number of interrupts taken */
+       __u64 sps_ints;
+       /* number of interrupts for errors */
+       __u64 sps_errints;
+       /* number of errors from chip (not incl. packet errors or CRC) */
+       __u64 sps_errs;
+       /* number of packet errors from chip other than CRC */
+       __u64 sps_pkterrs;
+       /* number of packets with CRC errors (ICRC and VCRC) */
+       __u64 sps_crcerrs;
+       /* number of hardware errors reported (parity, etc.) */
+       __u64 sps_hwerrs;
+       /* number of times IB link changed state unexpectedly */
+       __u64 sps_iblink;
+       /* no longer used; left for compatibility */
+       __u64 sps_unused3;
+       /* number of kernel (port0) packets received */
+       __u64 sps_port0pkts;
+       /* number of "ethernet" packets sent by driver */
+       __u64 sps_ether_spkts;
+       /* number of "ethernet" packets received by driver */
+       __u64 sps_ether_rpkts;
+       /* number of SMA packets sent by driver */
+       __u64 sps_sma_spkts;
+       /* number of SMA packets received by driver */
+       __u64 sps_sma_rpkts;
+       /* number of times all ports rcvhdrq was full and packet dropped */
+       __u64 sps_hdrqfull;
+       /* number of times all ports egrtid was full and packet dropped */
+       __u64 sps_etidfull;
+       /*
+        * number of times we tried to send from driver, but no pio buffers
+        * avail
+        */
+       __u64 sps_nopiobufs;
+       /* number of ports currently open */
+       __u64 sps_ports;
+       /* list of pkeys (other than default) accepted (0 means not set) */
+       __u16 sps_pkeys[4];
+       /* lids for up to 4 infinipaths, indexed by infinipath # */
+       __u16 sps_lid[4];
+       /* number of user ports per chip (not IB ports) */
+       __u32 sps_nports;
+       /* not our interrupt, or already handled */
+       __u32 sps_nullintr;
+       /* max number of packets handled per receive call */
+       __u32 sps_maxpkts_call;
+       /* avg number of packets handled per receive call */
+       __u32 sps_avgpkts_call;
+       /* total number of pages locked */
+       __u64 sps_pagelocks;
+       /* total number of pages unlocked */
+       __u64 sps_pageunlocks;
+       /*
+        * Number of packets dropped in kernel other than errors (ether
+        * packets if ipath not configured, sma/mad, etc.)
+        */
+       __u64 sps_krdrops;
+       /* mlids for up to 4 infinipaths, indexed by infinipath # */
+       __u16 sps_mlid[4];
+       /* pad for future growth */
+       __u64 __sps_pad[45];
+};
+
+/*
+ * These are the status bits readable (in ascii form, 64bit value)
+ * from the "status" sysfs file.
+ */
+#define IPATH_STATUS_INITTED       0x1 /* basic initialization done */
+#define IPATH_STATUS_DISABLED      0x2 /* hardware disabled */
+/* Device has been disabled via admin request */
+#define IPATH_STATUS_ADMIN_DISABLED    0x4
+#define IPATH_STATUS_OIB_SMA       0x8 /* ipath_mad kernel SMA running */
+#define IPATH_STATUS_SMA          0x10 /* user SMA running */
+/* Chip has been found and initted */
+#define IPATH_STATUS_CHIP_PRESENT 0x20
+/* IB link is at ACTIVE, usable for data traffic */
+#define IPATH_STATUS_IB_READY     0x40
+/* link is configured, LID, MTU, etc. have been set */
+#define IPATH_STATUS_IB_CONF      0x80
+/* no link established, probably no cable */
+#define IPATH_STATUS_IB_NOCABLE  0x100
+/* A Fatal hardware error has occurred. */
+#define IPATH_STATUS_HWERROR     0x200
+
+/*
+ * The list of usermode accessible registers.  Also see Reg_* later in file.
+ */
+typedef enum _ipath_ureg {
+       /* (RO)  DMA RcvHdr to be used next. */
+       ur_rcvhdrtail = 0,
+       /* (RW)  RcvHdr entry to be processed next by host. */
+       ur_rcvhdrhead = 1,
+       /* (RO)  Index of next Eager index to use. */
+       ur_rcvegrindextail = 2,
+       /* (RW)  Eager TID to be processed next */
+       ur_rcvegrindexhead = 3,
+       /* For internal use only; max register number. */
+       _IPATH_UregMax
+} ipath_ureg;
+
+/* bit values for spi_runtime_flags */
+#define IPATH_RUNTIME_HT       0x1
+#define IPATH_RUNTIME_PCIE     0x2
+#define IPATH_RUNTIME_FORCE_WC_ORDER   0x4
+#define IPATH_RUNTIME_RCVHDR_COPY      0x8
+
+/*
+ * This structure is returned by ipath_userinit() immediately after
+ * open to get implementation-specific info, and info specific to this
+ * instance.
+ *
+ * This struct must have explict pad fields where type sizes
+ * may result in different alignments between 32 and 64 bit
+ * programs, since the 64 bit * bit kernel requires the user code
+ * to have matching offsets
+ */
+struct ipath_base_info {
+       /* version of hardware, for feature checking. */
+       __u32 spi_hw_version;
+       /* version of software, for feature checking. */
+       __u32 spi_sw_version;
+       /* InfiniPath port assigned, goes into sent packets */
+       __u32 spi_port;
+       /*
+        * IB MTU, packets IB data must be less than this.
+        * The MTU is in bytes, and will be a multiple of 4 bytes.
+        */
+       __u32 spi_mtu;
+       /*
+        * Size of a PIO buffer.  Any given packet's total size must be less
+        * than this (in words).  Included is the starting control word, so
+        * if 513 is returned, then total pkt size is 512 words or less.
+        */
+       __u32 spi_piosize;
+       /* size of the TID cache in infinipath, in entries */
+       __u32 spi_tidcnt;
+       /* size of the TID Eager list in infinipath, in entries */
+       __u32 spi_tidegrcnt;
+       /* size of a single receive header queue entry. */
+       __u32 spi_rcvhdrent_size;
+       /*
+        * Count of receive header queue entries allocated.
+        * This may be less than the spu_rcvhdrcnt passed in!.
+        */
+       __u32 spi_rcvhdr_cnt;
+
+       /* per-chip and other runtime features bitmap (IPATH_RUNTIME_*) */
+       __u32 spi_runtime_flags;
+
+       /* address where receive buffer queue is mapped into */
+       __u64 spi_rcvhdr_base;
+
+       /* user program. */
+
+       /* base address of eager TID receive buffers. */
+       __u64 spi_rcv_egrbufs;
+
+       /* Allocated by initialization code, not by protocol. */
+
+       /*
+        * Size of each TID buffer in host memory, starting at
+        * spi_rcv_egrbufs.  The buffers are virtually contiguous.
+        */
+       __u32 spi_rcv_egrbufsize;
+       /*
+        * The special QP (queue pair) value that identifies an infinipath
+        * protocol packet from standard IB packets.  More, probably much
+        * more, to be added.
+        */
+       __u32 spi_qpair;
+
+       /*
+        * User register base for init code, not to be used directly by
+        * protocol or applications.
+        */
+       __u64 __spi_uregbase;
+       /*
+        * Maximum buffer size in bytes that can be used in a single TID
+        * entry (assuming the buffer is aligned to this boundary).  This is
+        * the minimum of what the hardware and software support Guaranteed
+        * to be a power of 2.
+        */
+       __u32 spi_tid_maxsize;
+       /*
+        * alignment of each pio send buffer (byte count
+        * to add to spi_piobufbase to get to second buffer)
+        */
+       __u32 spi_pioalign;
+       /*
+        * The index of the first pio buffer available to this process;
+        * needed to do lookup in spi_pioavailaddr; not added to
+        * spi_piobufbase.
+        */
+       __u32 spi_pioindex;
+        /* number of buffers mapped for this process */
+       __u32 spi_piocnt;
+
+       /*
+        * Base address of writeonly pio buffers for this process.
+        * Each buffer has spi_piosize words, and is aligned on spi_pioalign
+        * boundaries.  spi_piocnt buffers are mapped from this address
+        */
+       __u64 spi_piobufbase;
+
+       /*
+        * Base address of readonly memory copy of the pioavail registers.
+        * There are 2 bits for each buffer.
+        */
+       __u64 spi_pioavailaddr;
+
+       /*
+        * Address where driver updates a copy of the interface and driver
+        * status (IPATH_STATUS_*) as a 64 bit value.  It's followed by a
+        * string indicating hardware error, if there was one.
+        */
+       __u64 spi_status;
+
+       /* number of chip ports available to user processes */
+       __u32 spi_nports;
+       /* unit number of chip we are using */
+       __u32 spi_unit;
+       /* num bufs in each contiguous set */
+       __u32 spi_rcv_egrperchunk;
+       /* size in bytes of each contiguous set */
+       __u32 spi_rcv_egrchunksize;
+       /* total size of mmap to cover full rcvegrbuffers */
+       __u32 spi_rcv_egrbuftotlen;
+} __attribute__ ((aligned(8)));
+
+
+/*
+ * This version number is given to the driver by the user code during
+ * initialization in the spu_userversion field of ipath_user_info, so
+ * the driver can check for compatibility with user code.
+ *
+ * The major version changes when data structures
+ * change in an incompatible way.  The driver must be the same or higher
+ * for initialization to succeed.  In some cases, a higher version
+ * driver will not interoperate with older software, and initialization
+ * will return an error.
+ */
+#define IPATH_USER_SWMAJOR 1
+
+/*
+ * Minor version differences are always compatible
+ * a within a major version, however if if user software is larger
+ * than driver software, some new features and/or structure fields
+ * may not be implemented; the user code must deal with this if it
+ * cares, or it must abort after initialization reports the difference
+ */
+#define IPATH_USER_SWMINOR 2
+
+#define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR)
+
+#define IPATH_KERN_TYPE 0
+
+/*
+ * Similarly, this is the kernel version going back to the user.  It's
+ * slightly different, in that we want to tell if the driver was built as
+ * part of a PathScale release, or from the driver from OpenIB, kernel.org,
+ * or a standard distribution, for support reasons.  The high bit is 0 for
+ * non-PathScale, and 1 for PathScale-built/supplied.
+ *
+ * It's returned by the driver to the user code during initialization in the
+ * spi_sw_version field of ipath_base_info, so the user code can in turn
+ * check for compatibility with the kernel.
+*/
+#define IPATH_KERN_SWVERSION ((IPATH_KERN_TYPE<<31) | IPATH_USER_SWVERSION)
+
+/*
+ * This structure is passed to ipath_userinit() to tell the driver where
+ * user code buffers are, sizes, etc.   The offsets and sizes of the
+ * fields must remain unchanged, for binary compatibility.  It can
+ * be extended, if userversion is changed so user code can tell, if needed
+ */
+struct ipath_user_info {
+       /*
+        * version of user software, to detect compatibility issues.
+        * Should be set to IPATH_USER_SWVERSION.
+        */
+       __u32 spu_userversion;
+
+       /* desired number of receive header queue entries */
+       __u32 spu_rcvhdrcnt;
+
+       /* size of struct base_info to write to */
+       __u32 spu_base_info_size;
+
+       /*
+        * number of words in KD protocol header
+        * This tells InfiniPath how many words to copy to rcvhdrq.  If 0,
+        * kernel uses a default.  Once set, attempts to set any other value
+        * are an error (EAGAIN) until driver is reloaded.
+        */
+       __u32 spu_rcvhdrsize;
+
+       /*
+        * cache line aligned (64 byte) user address to
+        * which the rcvhdrtail register will be written by infinipath
+        * whenever it changes, so that no chip registers are read in
+        * the performance path.
+        */
+       __u64 spu_rcvhdraddr;
+
+       /*
+        * address of struct base_info to write to
+        */
+       __u64 spu_base_info;
+
+} __attribute__ ((aligned(8)));
+
+/* User commands. */
+
+#define IPATH_CMD_MIN          16
+
+#define IPATH_CMD_USER_INIT    16      /* set up userspace */
+#define IPATH_CMD_PORT_INFO    17      /* find out what resources we got */
+#define IPATH_CMD_RECV_CTRL    18      /* control receipt of packets */
+#define IPATH_CMD_TID_UPDATE   19      /* update expected TID entries */
+#define IPATH_CMD_TID_FREE     20      /* free expected TID entries */
+#define IPATH_CMD_SET_PART_KEY 21      /* add partition key */
+
+#define IPATH_CMD_MAX          21
+
+struct ipath_port_info {
+       __u32 num_active;       /* number of active units */
+       __u32 unit;             /* unit (chip) assigned to caller */
+       __u32 port;             /* port on unit assigned to caller */
+};
+
+struct ipath_tid_info {
+       __u32 tidcnt;
+       /* make structure same size in 32 and 64 bit */
+       __u32 tid__unused;
+       /* virtual address of first page in transfer */
+       __u64 tidvaddr;
+       /* pointer (same size 32/64 bit) to __u16 tid array */
+       __u64 tidlist;
+
+       /*
+        * pointer (same size 32/64 bit) to bitmap of TIDs used
+        * for this call; checked for being large enough at open
+        */
+       __u64 tidmap;
+};
+
+struct ipath_cmd {
+       __u32 type;                     /* command type */
+       union {
+               struct ipath_tid_info tid_info;
+               struct ipath_user_info user_info;
+               /* address in userspace of struct ipath_port_info to
+                  write result to */
+               __u64 port_info;
+               /* enable/disable receipt of packets */
+               __u32 recv_ctrl;
+               /* partition key to set */
+               __u16 part_key;
+       } cmd;
+};
+
+struct ipath_iovec {
+       /* Pointer to data, but same size 32 and 64 bit */
+       __u64 iov_base;
+
+       /*
+        * Length of data; don't need 64 bits, but want
+        * ipath_sendpkt to remain same size as before 32 bit changes, so...
+        */
+       __u64 iov_len;
+};
+
+/*
+ * Describes a single packet for send.  Each packet can have one or more
+ * buffers, but the total length (exclusive of IB headers) must be less
+ * than the MTU, and if using the PIO method, entire packet length,
+ * including IB headers, must be less than the ipath_piosize value (words).
+ * Use of this necessitates including sys/uio.h
+ */
+struct __ipath_sendpkt {
+       __u32 sps_flags;        /* flags for packet (TBD) */
+       __u32 sps_cnt;          /* number of entries to use in sps_iov */
+       /* array of iov's describing packet. TEMPORARY */
+       struct ipath_iovec sps_iov[4];
+};
+
+/* Passed into SMA special file's ->read and ->write methods. */
+struct ipath_sma_pkt
+{
+       __u32 unit;     /* unit on which to send packet */
+       __u64 data;     /* address of payload in userspace */
+       __u32 len;      /* length of payload */
+};
+
+/*
+ * Data layout in I2C flash (for GUID, etc.)
+ * All fields are little-endian binary unless otherwise stated
+ */
+#define IPATH_FLASH_VERSION 1
+struct ipath_flash {
+       /* flash layout version (IPATH_FLASH_VERSION) */
+       __u8 if_fversion;
+       /* checksum protecting if_length bytes */
+       __u8 if_csum;
+       /*
+        * valid length (in use, protected by if_csum), including
+        * if_fversion and if_sum themselves)
+        */
+       __u8 if_length;
+       /* the GUID, in network order */
+       __u8 if_guid[8];
+       /* number of GUIDs to use, starting from if_guid */
+       __u8 if_numguid;
+       /* the board serial number, in ASCII */
+       char if_serial[12];
+       /* board mfg date (YYYYMMDD ASCII) */
+       char if_mfgdate[8];
+       /* last board rework/test date (YYYYMMDD ASCII) */
+       char if_testdate[8];
+       /* logging of error counts, TBD */
+       __u8 if_errcntp[4];
+       /* powered on hours, updated at driver unload */
+       __u8 if_powerhour[2];
+       /* ASCII free-form comment field */
+       char if_comment[32];
+       /* 78 bytes used, min flash size is 128 bytes */
+       __u8 if_future[50];
+};
+
+/*
+ * These are the counters implemented in the chip, and are listed in order.
+ * The InterCaps naming is taken straight from the chip spec.
+ */
+struct infinipath_counters {
+       __u64 LBIntCnt;
+       __u64 LBFlowStallCnt;
+       __u64 Reserved1;
+       __u64 TxUnsupVLErrCnt;
+       __u64 TxDataPktCnt;
+       __u64 TxFlowPktCnt;
+       __u64 TxDwordCnt;
+       __u64 TxLenErrCnt;
+       __u64 TxMaxMinLenErrCnt;
+       __u64 TxUnderrunCnt;
+       __u64 TxFlowStallCnt;
+       __u64 TxDroppedPktCnt;
+       __u64 RxDroppedPktCnt;
+       __u64 RxDataPktCnt;
+       __u64 RxFlowPktCnt;
+       __u64 RxDwordCnt;
+       __u64 RxLenErrCnt;
+       __u64 RxMaxMinLenErrCnt;
+       __u64 RxICRCErrCnt;
+       __u64 RxVCRCErrCnt;
+       __u64 RxFlowCtrlErrCnt;
+       __u64 RxBadFormatCnt;
+       __u64 RxLinkProblemCnt;
+       __u64 RxEBPCnt;
+       __u64 RxLPCRCErrCnt;
+       __u64 RxBufOvflCnt;
+       __u64 RxTIDFullErrCnt;
+       __u64 RxTIDValidErrCnt;
+       __u64 RxPKeyMismatchCnt;
+       __u64 RxP0HdrEgrOvflCnt;
+       __u64 RxP1HdrEgrOvflCnt;
+       __u64 RxP2HdrEgrOvflCnt;
+       __u64 RxP3HdrEgrOvflCnt;
+       __u64 RxP4HdrEgrOvflCnt;
+       __u64 RxP5HdrEgrOvflCnt;
+       __u64 RxP6HdrEgrOvflCnt;
+       __u64 RxP7HdrEgrOvflCnt;
+       __u64 RxP8HdrEgrOvflCnt;
+       __u64 Reserved6;
+       __u64 Reserved7;
+       __u64 IBStatusChangeCnt;
+       __u64 IBLinkErrRecoveryCnt;
+       __u64 IBLinkDownedCnt;
+       __u64 IBSymbolErrCnt;
+};
+
+/*
+ * The next set of defines are for packet headers, and chip register
+ * and memory bits that are visible to and/or used by user-mode software
+ * The other bits that are used only by the driver or diags are in
+ * ipath_registers.h
+ */
+
+/* RcvHdrFlags bits */
+#define INFINIPATH_RHF_LENGTH_MASK 0x7FF
+#define INFINIPATH_RHF_LENGTH_SHIFT 0
+#define INFINIPATH_RHF_RCVTYPE_MASK 0x7
+#define INFINIPATH_RHF_RCVTYPE_SHIFT 11
+#define INFINIPATH_RHF_EGRINDEX_MASK 0x7FF
+#define INFINIPATH_RHF_EGRINDEX_SHIFT 16
+#define INFINIPATH_RHF_H_ICRCERR   0x80000000
+#define INFINIPATH_RHF_H_VCRCERR   0x40000000
+#define INFINIPATH_RHF_H_PARITYERR 0x20000000
+#define INFINIPATH_RHF_H_LENERR    0x10000000
+#define INFINIPATH_RHF_H_MTUERR    0x08000000
+#define INFINIPATH_RHF_H_IHDRERR   0x04000000
+#define INFINIPATH_RHF_H_TIDERR    0x02000000
+#define INFINIPATH_RHF_H_MKERR     0x01000000
+#define INFINIPATH_RHF_H_IBERR     0x00800000
+#define INFINIPATH_RHF_L_SWA       0x00008000
+#define INFINIPATH_RHF_L_SWB       0x00004000
+
+/* infinipath header fields */
+#define INFINIPATH_I_VERS_MASK 0xF
+#define INFINIPATH_I_VERS_SHIFT 28
+#define INFINIPATH_I_PORT_MASK 0xF
+#define INFINIPATH_I_PORT_SHIFT 24
+#define INFINIPATH_I_TID_MASK 0x7FF
+#define INFINIPATH_I_TID_SHIFT 13
+#define INFINIPATH_I_OFFSET_MASK 0x1FFF
+#define INFINIPATH_I_OFFSET_SHIFT 0
+
+/* K_PktFlags bits */
+#define INFINIPATH_KPF_INTR 0x1
+
+/* SendPIO per-buffer control */
+#define INFINIPATH_SP_LENGTHP1_MASK 0x3FF
+#define INFINIPATH_SP_LENGTHP1_SHIFT 0
+#define INFINIPATH_SP_INTR    0x80000000
+#define INFINIPATH_SP_TEST    0x40000000
+#define INFINIPATH_SP_TESTEBP 0x20000000
+
+/* SendPIOAvail bits */
+#define INFINIPATH_SENDPIOAVAIL_BUSY_SHIFT 1
+#define INFINIPATH_SENDPIOAVAIL_CHECK_SHIFT 0
+
+#endif                         /* _IPATH_COMMON_H */
diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c
new file mode 100644 (file)
index 0000000..7ece113
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/err.h>
+#include <linux/vmalloc.h>
+
+#include "ipath_verbs.h"
+
+/**
+ * ipath_cq_enter - add a new entry to the completion queue
+ * @cq: completion queue
+ * @entry: work completion entry to add
+ * @sig: true if @entry is a solicitated entry
+ *
+ * This may be called with one of the qp->s_lock or qp->r_rq.lock held.
+ */
+void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited)
+{
+       unsigned long flags;
+       u32 next;
+
+       spin_lock_irqsave(&cq->lock, flags);
+
+       if (cq->head == cq->ibcq.cqe)
+               next = 0;
+       else
+               next = cq->head + 1;
+       if (unlikely(next == cq->tail)) {
+               spin_unlock_irqrestore(&cq->lock, flags);
+               if (cq->ibcq.event_handler) {
+                       struct ib_event ev;
+
+                       ev.device = cq->ibcq.device;
+                       ev.element.cq = &cq->ibcq;
+                       ev.event = IB_EVENT_CQ_ERR;
+                       cq->ibcq.event_handler(&ev, cq->ibcq.cq_context);
+               }
+               return;
+       }
+       cq->queue[cq->head] = *entry;
+       cq->head = next;
+
+       if (cq->notify == IB_CQ_NEXT_COMP ||
+           (cq->notify == IB_CQ_SOLICITED && solicited)) {
+               cq->notify = IB_CQ_NONE;
+               cq->triggered++;
+               /*
+                * This will cause send_complete() to be called in
+                * another thread.
+                */
+               tasklet_hi_schedule(&cq->comptask);
+       }
+
+       spin_unlock_irqrestore(&cq->lock, flags);
+
+       if (entry->status != IB_WC_SUCCESS)
+               to_idev(cq->ibcq.device)->n_wqe_errs++;
+}
+
+/**
+ * ipath_poll_cq - poll for work completion entries
+ * @ibcq: the completion queue to poll
+ * @num_entries: the maximum number of entries to return
+ * @entry: pointer to array where work completions are placed
+ *
+ * Returns the number of completion entries polled.
+ *
+ * This may be called from interrupt context.  Also called by ib_poll_cq()
+ * in the generic verbs code.
+ */
+int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
+{
+       struct ipath_cq *cq = to_icq(ibcq);
+       unsigned long flags;
+       int npolled;
+
+       spin_lock_irqsave(&cq->lock, flags);
+
+       for (npolled = 0; npolled < num_entries; ++npolled, ++entry) {
+               if (cq->tail == cq->head)
+                       break;
+               *entry = cq->queue[cq->tail];
+               if (cq->tail == cq->ibcq.cqe)
+                       cq->tail = 0;
+               else
+                       cq->tail++;
+       }
+
+       spin_unlock_irqrestore(&cq->lock, flags);
+
+       return npolled;
+}
+
+static void send_complete(unsigned long data)
+{
+       struct ipath_cq *cq = (struct ipath_cq *)data;
+
+       /*
+        * The completion handler will most likely rearm the notification
+        * and poll for all pending entries.  If a new completion entry
+        * is added while we are in this routine, tasklet_hi_schedule()
+        * won't call us again until we return so we check triggered to
+        * see if we need to call the handler again.
+        */
+       for (;;) {
+               u8 triggered = cq->triggered;
+
+               cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
+
+               if (cq->triggered == triggered)
+                       return;
+       }
+}
+
+/**
+ * ipath_create_cq - create a completion queue
+ * @ibdev: the device this completion queue is attached to
+ * @entries: the minimum size of the completion queue
+ * @context: unused by the InfiniPath driver
+ * @udata: unused by the InfiniPath driver
+ *
+ * Returns a pointer to the completion queue or negative errno values
+ * for failure.
+ *
+ * Called by ib_create_cq() in the generic verbs code.
+ */
+struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries,
+                             struct ib_ucontext *context,
+                             struct ib_udata *udata)
+{
+       struct ipath_cq *cq;
+       struct ib_wc *wc;
+       struct ib_cq *ret;
+
+       /*
+        * Need to use vmalloc() if we want to support large #s of
+        * entries.
+        */
+       cq = kmalloc(sizeof(*cq), GFP_KERNEL);
+       if (!cq) {
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       /*
+        * Need to use vmalloc() if we want to support large #s of entries.
+        */
+       wc = vmalloc(sizeof(*wc) * (entries + 1));
+       if (!wc) {
+               kfree(cq);
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+       /*
+        * ib_create_cq() will initialize cq->ibcq except for cq->ibcq.cqe.
+        * The number of entries should be >= the number requested or return
+        * an error.
+        */
+       cq->ibcq.cqe = entries;
+       cq->notify = IB_CQ_NONE;
+       cq->triggered = 0;
+       spin_lock_init(&cq->lock);
+       tasklet_init(&cq->comptask, send_complete, (unsigned long)cq);
+       cq->head = 0;
+       cq->tail = 0;
+       cq->queue = wc;
+
+       ret = &cq->ibcq;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_destroy_cq - destroy a completion queue
+ * @ibcq: the completion queue to destroy.
+ *
+ * Returns 0 for success.
+ *
+ * Called by ib_destroy_cq() in the generic verbs code.
+ */
+int ipath_destroy_cq(struct ib_cq *ibcq)
+{
+       struct ipath_cq *cq = to_icq(ibcq);
+
+       tasklet_kill(&cq->comptask);
+       vfree(cq->queue);
+       kfree(cq);
+
+       return 0;
+}
+
+/**
+ * ipath_req_notify_cq - change the notification type for a completion queue
+ * @ibcq: the completion queue
+ * @notify: the type of notification to request
+ *
+ * Returns 0 for success.
+ *
+ * This may be called from interrupt context.  Also called by
+ * ib_req_notify_cq() in the generic verbs code.
+ */
+int ipath_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify notify)
+{
+       struct ipath_cq *cq = to_icq(ibcq);
+       unsigned long flags;
+
+       spin_lock_irqsave(&cq->lock, flags);
+       /*
+        * Don't change IB_CQ_NEXT_COMP to IB_CQ_SOLICITED but allow
+        * any other transitions.
+        */
+       if (cq->notify != IB_CQ_NEXT_COMP)
+               cq->notify = notify;
+       spin_unlock_irqrestore(&cq->lock, flags);
+       return 0;
+}
+
+int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
+{
+       struct ipath_cq *cq = to_icq(ibcq);
+       struct ib_wc *wc, *old_wc;
+       u32 n;
+       int ret;
+
+       /*
+        * Need to use vmalloc() if we want to support large #s of entries.
+        */
+       wc = vmalloc(sizeof(*wc) * (cqe + 1));
+       if (!wc) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+
+       spin_lock_irq(&cq->lock);
+       if (cq->head < cq->tail)
+               n = cq->ibcq.cqe + 1 + cq->head - cq->tail;
+       else
+               n = cq->head - cq->tail;
+       if (unlikely((u32)cqe < n)) {
+               spin_unlock_irq(&cq->lock);
+               vfree(wc);
+               ret = -EOVERFLOW;
+               goto bail;
+       }
+       for (n = 0; cq->tail != cq->head; n++) {
+               wc[n] = cq->queue[cq->tail];
+               if (cq->tail == cq->ibcq.cqe)
+                       cq->tail = 0;
+               else
+                       cq->tail++;
+       }
+       cq->ibcq.cqe = cqe;
+       cq->head = n;
+       cq->tail = 0;
+       old_wc = cq->queue;
+       cq->queue = wc;
+       spin_unlock_irq(&cq->lock);
+
+       vfree(old_wc);
+
+       ret = 0;
+
+bail:
+       return ret;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_debug.h b/drivers/infiniband/hw/ipath/ipath_debug.h
new file mode 100644 (file)
index 0000000..593e289
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _IPATH_DEBUG_H
+#define _IPATH_DEBUG_H
+
+#ifndef _IPATH_DEBUGGING       /* debugging enabled or not */
+#define _IPATH_DEBUGGING 1
+#endif
+
+#if _IPATH_DEBUGGING
+
+/*
+ * Mask values for debugging.  The scheme allows us to compile out any
+ * of the debug tracing stuff, and if compiled in, to enable or disable
+ * dynamically.  This can be set at modprobe time also:
+ *      modprobe infinipath.ko infinipath_debug=7
+ */
+
+#define __IPATH_INFO        0x1        /* generic low verbosity stuff */
+#define __IPATH_DBG         0x2        /* generic debug */
+#define __IPATH_TRSAMPLE    0x8        /* generate trace buffer sample entries */
+/* leave some low verbosity spots open */
+#define __IPATH_VERBDBG     0x40       /* very verbose debug */
+#define __IPATH_PKTDBG      0x80       /* print packet data */
+/* print process startup (init)/exit messages */
+#define __IPATH_PROCDBG     0x100
+/* print mmap/nopage stuff, not using VDBG any more */
+#define __IPATH_MMDBG       0x200
+#define __IPATH_USER_SEND   0x1000     /* use user mode send */
+#define __IPATH_KERNEL_SEND 0x2000     /* use kernel mode send */
+#define __IPATH_EPKTDBG     0x4000     /* print ethernet packet data */
+#define __IPATH_SMADBG      0x8000     /* sma packet debug */
+#define __IPATH_IPATHDBG    0x10000    /* Ethernet (IPATH) general debug on */
+#define __IPATH_IPATHWARN   0x20000    /* Ethernet (IPATH) warnings on */
+#define __IPATH_IPATHERR    0x40000    /* Ethernet (IPATH) errors on */
+#define __IPATH_IPATHPD     0x80000    /* Ethernet (IPATH) packet dump on */
+#define __IPATH_IPATHTABLE  0x100000   /* Ethernet (IPATH) table dump on */
+
+#else                          /* _IPATH_DEBUGGING */
+
+/*
+ * define all of these even with debugging off, for the few places that do
+ * if(infinipath_debug & _IPATH_xyzzy), but in a way that will make the
+ * compiler eliminate the code
+ */
+
+#define __IPATH_INFO      0x0  /* generic low verbosity stuff */
+#define __IPATH_DBG       0x0  /* generic debug */
+#define __IPATH_TRSAMPLE  0x0  /* generate trace buffer sample entries */
+#define __IPATH_VERBDBG   0x0  /* very verbose debug */
+#define __IPATH_PKTDBG    0x0  /* print packet data */
+#define __IPATH_PROCDBG   0x0  /* print process startup (init)/exit messages */
+/* print mmap/nopage stuff, not using VDBG any more */
+#define __IPATH_MMDBG     0x0
+#define __IPATH_EPKTDBG   0x0  /* print ethernet packet data */
+#define __IPATH_SMADBG    0x0   /* print process startup (init)/exit messages */#define __IPATH_IPATHDBG  0x0  /* Ethernet (IPATH) table dump on */
+#define __IPATH_IPATHWARN 0x0  /* Ethernet (IPATH) warnings on   */
+#define __IPATH_IPATHERR  0x0  /* Ethernet (IPATH) errors on   */
+#define __IPATH_IPATHPD   0x0  /* Ethernet (IPATH) packet dump on   */
+#define __IPATH_IPATHTABLE 0x0 /* Ethernet (IPATH) packet dump on   */
+
+#endif                         /* _IPATH_DEBUGGING */
+
+#define __IPATH_VERBOSEDBG __IPATH_VERBDBG
+
+#endif                         /* _IPATH_DEBUG_H */
diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c
new file mode 100644 (file)
index 0000000..cd533cf
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * This file contains support for diagnostic functions.  It is accessed by
+ * opening the ipath_diag device, normally minor number 129.  Diagnostic use
+ * of the InfiniPath chip may render the chip or board unusable until the
+ * driver is unloaded, or in some cases, until the system is rebooted.
+ *
+ * Accesses to the chip through this interface are not similar to going
+ * through the /sys/bus/pci resource mmap interface.
+ */
+
+#include <linux/pci.h>
+#include <asm/uaccess.h>
+
+#include "ipath_common.h"
+#include "ipath_kernel.h"
+#include "ips_common.h"
+#include "ipath_layer.h"
+
+int ipath_diag_inuse;
+static int diag_set_link;
+
+static int ipath_diag_open(struct inode *in, struct file *fp);
+static int ipath_diag_release(struct inode *in, struct file *fp);
+static ssize_t ipath_diag_read(struct file *fp, char __user *data,
+                              size_t count, loff_t *off);
+static ssize_t ipath_diag_write(struct file *fp, const char __user *data,
+                               size_t count, loff_t *off);
+
+static struct file_operations diag_file_ops = {
+       .owner = THIS_MODULE,
+       .write = ipath_diag_write,
+       .read = ipath_diag_read,
+       .open = ipath_diag_open,
+       .release = ipath_diag_release
+};
+
+static struct cdev *diag_cdev;
+static struct class_device *diag_class_dev;
+
+int ipath_diag_init(void)
+{
+       return ipath_cdev_init(IPATH_DIAG_MINOR, "ipath_diag",
+                              &diag_file_ops, &diag_cdev, &diag_class_dev);
+}
+
+void ipath_diag_cleanup(void)
+{
+       ipath_cdev_cleanup(&diag_cdev, &diag_class_dev);
+}
+
+/**
+ * ipath_read_umem64 - read a 64-bit quantity from the chip into user space
+ * @dd: the infinipath device
+ * @uaddr: the location to store the data in user memory
+ * @caddr: the source chip address (full pointer, not offset)
+ * @count: number of bytes to copy (multiple of 32 bits)
+ *
+ * This function also localizes all chip memory accesses.
+ * The copy should be written such that we read full cacheline packets
+ * from the chip.  This is usually used for a single qword
+ *
+ * NOTE:  This assumes the chip address is 64-bit aligned.
+ */
+static int ipath_read_umem64(struct ipath_devdata *dd, void __user *uaddr,
+                            const void __iomem *caddr, size_t count)
+{
+       const u64 __iomem *reg_addr = caddr;
+       const u64 __iomem *reg_end = reg_addr + (count / sizeof(u64));
+       int ret;
+
+       /* not very efficient, but it works for now */
+       if (reg_addr < dd->ipath_kregbase ||
+           reg_end > dd->ipath_kregend) {
+               ret = -EINVAL;
+               goto bail;
+       }
+       while (reg_addr < reg_end) {
+               u64 data = readq(reg_addr);
+               if (copy_to_user(uaddr, &data, sizeof(u64))) {
+                       ret = -EFAULT;
+                       goto bail;
+               }
+               reg_addr++;
+               uaddr++;
+       }
+       ret = 0;
+bail:
+       return ret;
+}
+
+/**
+ * ipath_write_umem64 - write a 64-bit quantity to the chip from user space
+ * @dd: the infinipath device
+ * @caddr: the destination chip address (full pointer, not offset)
+ * @uaddr: the source of the data in user memory
+ * @count: the number of bytes to copy (multiple of 32 bits)
+ *
+ * This is usually used for a single qword
+ * NOTE:  This assumes the chip address is 64-bit aligned.
+ */
+
+static int ipath_write_umem64(struct ipath_devdata *dd, void __iomem *caddr,
+                             const void __user *uaddr, size_t count)
+{
+       u64 __iomem *reg_addr = caddr;
+       const u64 __iomem *reg_end = reg_addr + (count / sizeof(u64));
+       int ret;
+
+       /* not very efficient, but it works for now */
+       if (reg_addr < dd->ipath_kregbase ||
+           reg_end > dd->ipath_kregend) {
+               ret = -EINVAL;
+               goto bail;
+       }
+       while (reg_addr < reg_end) {
+               u64 data;
+               if (copy_from_user(&data, uaddr, sizeof(data))) {
+                       ret = -EFAULT;
+                       goto bail;
+               }
+               writeq(data, reg_addr);
+
+               reg_addr++;
+               uaddr++;
+       }
+       ret = 0;
+bail:
+       return ret;
+}
+
+/**
+ * ipath_read_umem32 - read a 32-bit quantity from the chip into user space
+ * @dd: the infinipath device
+ * @uaddr: the location to store the data in user memory
+ * @caddr: the source chip address (full pointer, not offset)
+ * @count: number of bytes to copy
+ *
+ * read 32 bit values, not 64 bit; for memories that only
+ * support 32 bit reads; usually a single dword.
+ */
+static int ipath_read_umem32(struct ipath_devdata *dd, void __user *uaddr,
+                            const void __iomem *caddr, size_t count)
+{
+       const u32 __iomem *reg_addr = caddr;
+       const u32 __iomem *reg_end = reg_addr + (count / sizeof(u32));
+       int ret;
+
+       if (reg_addr < (u32 __iomem *) dd->ipath_kregbase ||
+           reg_end > (u32 __iomem *) dd->ipath_kregend) {
+               ret = -EINVAL;
+               goto bail;
+       }
+       /* not very efficient, but it works for now */
+       while (reg_addr < reg_end) {
+               u32 data = readl(reg_addr);
+               if (copy_to_user(uaddr, &data, sizeof(data))) {
+                       ret = -EFAULT;
+                       goto bail;
+               }
+
+               reg_addr++;
+               uaddr++;
+       }
+       ret = 0;
+bail:
+       return ret;
+}
+
+/**
+ * ipath_write_umem32 - write a 32-bit quantity to the chip from user space
+ * @dd: the infinipath device
+ * @caddr: the destination chip address (full pointer, not offset)
+ * @uaddr: the source of the data in user memory
+ * @count: number of bytes to copy
+ *
+ * write 32 bit values, not 64 bit; for memories that only
+ * support 32 bit write; usually a single dword.
+ */
+
+static int ipath_write_umem32(struct ipath_devdata *dd, void __iomem *caddr,
+                             const void __user *uaddr, size_t count)
+{
+       u32 __iomem *reg_addr = caddr;
+       const u32 __iomem *reg_end = reg_addr + (count / sizeof(u32));
+       int ret;
+
+       if (reg_addr < (u32 __iomem *) dd->ipath_kregbase ||
+           reg_end > (u32 __iomem *) dd->ipath_kregend) {
+               ret = -EINVAL;
+               goto bail;
+       }
+       while (reg_addr < reg_end) {
+               u32 data;
+               if (copy_from_user(&data, uaddr, sizeof(data))) {
+                       ret = -EFAULT;
+                       goto bail;
+               }
+               writel(data, reg_addr);
+
+               reg_addr++;
+               uaddr++;
+       }
+       ret = 0;
+bail:
+       return ret;
+}
+
+static int ipath_diag_open(struct inode *in, struct file *fp)
+{
+       struct ipath_devdata *dd;
+       int unit = 0; /* XXX this is bogus */
+       unsigned long flags;
+       int ret;
+
+       dd = ipath_lookup(unit);
+
+       mutex_lock(&ipath_mutex);
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+
+       if (ipath_diag_inuse) {
+               ret = -EBUSY;
+               goto bail;
+       }
+
+       list_for_each_entry(dd, &ipath_dev_list, ipath_list) {
+               /*
+                * we need at least one infinipath device to be present
+                * (don't use INITTED, because we want to be able to open
+                * even if device is in freeze mode, which cleared INITTED).
+                * There is a small amount of risk to this, which is why we
+                * also verify kregbase is set.
+                */
+
+               if (!(dd->ipath_flags & IPATH_PRESENT) ||
+                   !dd->ipath_kregbase)
+                       continue;
+
+               ipath_diag_inuse = 1;
+               diag_set_link = 0;
+               ret = 0;
+               goto bail;
+       }
+
+       ret = -ENODEV;
+
+bail:
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+       mutex_unlock(&ipath_mutex);
+
+       /* Only expose a way to reset the device if we
+          make it into diag mode. */
+       if (ret == 0)
+               ipath_expose_reset(&dd->pcidev->dev);
+
+       return ret;
+}
+
+static int ipath_diag_release(struct inode *i, struct file *f)
+{
+       mutex_lock(&ipath_mutex);
+       ipath_diag_inuse = 0;
+       mutex_unlock(&ipath_mutex);
+       return 0;
+}
+
+static ssize_t ipath_diag_read(struct file *fp, char __user *data,
+                              size_t count, loff_t *off)
+{
+       int unit = 0; /* XXX provide for reads on other units some day */
+       struct ipath_devdata *dd;
+       void __iomem *kreg_base;
+       ssize_t ret;
+
+       dd = ipath_lookup(unit);
+       if (!dd) {
+               ret = -ENODEV;
+               goto bail;
+       }
+
+       kreg_base = dd->ipath_kregbase;
+
+       if (count == 0)
+               ret = 0;
+       else if ((count % 4) || (*off % 4))
+               /* address or length is not 32-bit aligned, hence invalid */
+               ret = -EINVAL;
+       else if ((count % 8) || (*off % 8))
+               /* address or length not 64-bit aligned; do 32-bit reads */
+               ret = ipath_read_umem32(dd, data, kreg_base + *off, count);
+       else
+               ret = ipath_read_umem64(dd, data, kreg_base + *off, count);
+
+       if (ret >= 0) {
+               *off += count;
+               ret = count;
+       }
+
+bail:
+       return ret;
+}
+
+static ssize_t ipath_diag_write(struct file *fp, const char __user *data,
+                               size_t count, loff_t *off)
+{
+       int unit = 0; /* XXX this is bogus */
+       struct ipath_devdata *dd;
+       void __iomem *kreg_base;
+       ssize_t ret;
+
+       dd = ipath_lookup(unit);
+       if (!dd) {
+               ret = -ENODEV;
+               goto bail;
+       }
+       kreg_base = dd->ipath_kregbase;
+
+       if (count == 0)
+               ret = 0;
+       else if ((count % 4) || (*off % 4))
+               /* address or length is not 32-bit aligned, hence invalid */
+               ret = -EINVAL;
+       else if ((count % 8) || (*off % 8))
+               /* address or length not 64-bit aligned; do 32-bit writes */
+               ret = ipath_write_umem32(dd, kreg_base + *off, data, count);
+       else
+               ret = ipath_write_umem64(dd, kreg_base + *off, data, count);
+
+       if (ret >= 0) {
+               *off += count;
+               ret = count;
+       }
+
+bail:
+       return ret;
+}
+
+void ipath_diag_bringup_link(struct ipath_devdata *dd)
+{
+       if (diag_set_link || (dd->ipath_flags & IPATH_LINKACTIVE))
+               return;
+
+       diag_set_link = 1;
+       ipath_cdbg(VERBOSE, "Trying to set to set link active for "
+                  "diag pkt\n");
+       ipath_layer_set_linkstate(dd, IPATH_IB_LINKARM);
+       ipath_layer_set_linkstate(dd, IPATH_IB_LINKACTIVE);
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
new file mode 100644 (file)
index 0000000..58a94ef
--- /dev/null
@@ -0,0 +1,1983 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/idr.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
+
+#include "ipath_kernel.h"
+#include "ips_common.h"
+#include "ipath_layer.h"
+
+static void ipath_update_pio_bufs(struct ipath_devdata *);
+
+const char *ipath_get_unit_name(int unit)
+{
+       static char iname[16];
+       snprintf(iname, sizeof iname, "infinipath%u", unit);
+       return iname;
+}
+
+EXPORT_SYMBOL_GPL(ipath_get_unit_name);
+
+#define DRIVER_LOAD_MSG "PathScale " IPATH_DRV_NAME " loaded: "
+#define PFX IPATH_DRV_NAME ": "
+
+/*
+ * The size has to be longer than this string, so we can append
+ * board/chip information to it in the init code.
+ */
+const char ipath_core_version[] = IPATH_IDSTR "\n";
+
+static struct idr unit_table;
+DEFINE_SPINLOCK(ipath_devs_lock);
+LIST_HEAD(ipath_dev_list);
+
+wait_queue_head_t ipath_sma_state_wait;
+
+unsigned ipath_debug = __IPATH_INFO;
+
+module_param_named(debug, ipath_debug, uint, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(debug, "mask for debug prints");
+EXPORT_SYMBOL_GPL(ipath_debug);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("PathScale <support@pathscale.com>");
+MODULE_DESCRIPTION("Pathscale InfiniPath driver");
+
+const char *ipath_ibcstatus_str[] = {
+       "Disabled",
+       "LinkUp",
+       "PollActive",
+       "PollQuiet",
+       "SleepDelay",
+       "SleepQuiet",
+       "LState6",              /* unused */
+       "LState7",              /* unused */
+       "CfgDebounce",
+       "CfgRcvfCfg",
+       "CfgWaitRmt",
+       "CfgIdle",
+       "RecovRetrain",
+       "LState0xD",            /* unused */
+       "RecovWaitRmt",
+       "RecovIdle",
+};
+
+/*
+ * These variables are initialized in the chip-specific files
+ * but are defined here.
+ */
+u16 ipath_gpio_sda_num, ipath_gpio_scl_num;
+u64 ipath_gpio_sda, ipath_gpio_scl;
+u64 infinipath_i_bitsextant;
+ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant;
+u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask;
+
+static void __devexit ipath_remove_one(struct pci_dev *);
+static int __devinit ipath_init_one(struct pci_dev *,
+                                   const struct pci_device_id *);
+
+/* Only needed for registration, nothing else needs this info */
+#define PCI_VENDOR_ID_PATHSCALE 0x1fc1
+#define PCI_DEVICE_ID_INFINIPATH_HT 0xd
+#define PCI_DEVICE_ID_INFINIPATH_PE800 0x10
+
+static const struct pci_device_id ipath_pci_tbl[] = {
+       {PCI_DEVICE(PCI_VENDOR_ID_PATHSCALE,
+                   PCI_DEVICE_ID_INFINIPATH_HT)},
+       {PCI_DEVICE(PCI_VENDOR_ID_PATHSCALE,
+                   PCI_DEVICE_ID_INFINIPATH_PE800)},
+};
+
+MODULE_DEVICE_TABLE(pci, ipath_pci_tbl);
+
+static struct pci_driver ipath_driver = {
+       .name = IPATH_DRV_NAME,
+       .probe = ipath_init_one,
+       .remove = __devexit_p(ipath_remove_one),
+       .id_table = ipath_pci_tbl,
+};
+
+/*
+ * This is where port 0's rcvhdrtail register is written back; we also
+ * want nothing else sharing the cache line, so make it a cache line
+ * in size.  Used for all units.
+ */
+volatile __le64 *ipath_port0_rcvhdrtail;
+dma_addr_t ipath_port0_rcvhdrtail_dma;
+static int port0_rcvhdrtail_refs;
+
+static inline void read_bars(struct ipath_devdata *dd, struct pci_dev *dev,
+                            u32 *bar0, u32 *bar1)
+{
+       int ret;
+
+       ret = pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, bar0);
+       if (ret)
+               ipath_dev_err(dd, "failed to read bar0 before enable: "
+                             "error %d\n", -ret);
+
+       ret = pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, bar1);
+       if (ret)
+               ipath_dev_err(dd, "failed to read bar1 before enable: "
+                             "error %d\n", -ret);
+
+       ipath_dbg("Read bar0 %x bar1 %x\n", *bar0, *bar1);
+}
+
+static void ipath_free_devdata(struct pci_dev *pdev,
+                              struct ipath_devdata *dd)
+{
+       unsigned long flags;
+
+       pci_set_drvdata(pdev, NULL);
+
+       if (dd->ipath_unit != -1) {
+               spin_lock_irqsave(&ipath_devs_lock, flags);
+               idr_remove(&unit_table, dd->ipath_unit);
+               list_del(&dd->ipath_list);
+               spin_unlock_irqrestore(&ipath_devs_lock, flags);
+       }
+       dma_free_coherent(&pdev->dev, sizeof(*dd), dd, dd->ipath_dma_addr);
+}
+
+static struct ipath_devdata *ipath_alloc_devdata(struct pci_dev *pdev)
+{
+       unsigned long flags;
+       struct ipath_devdata *dd;
+       dma_addr_t dma_addr;
+       int ret;
+
+       if (!idr_pre_get(&unit_table, GFP_KERNEL)) {
+               dd = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       dd = dma_alloc_coherent(&pdev->dev, sizeof(*dd), &dma_addr,
+                               GFP_KERNEL);
+
+       if (!dd) {
+               dd = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       dd->ipath_dma_addr = dma_addr;
+       dd->ipath_unit = -1;
+
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+
+       ret = idr_get_new(&unit_table, dd, &dd->ipath_unit);
+       if (ret < 0) {
+               printk(KERN_ERR IPATH_DRV_NAME
+                      ": Could not allocate unit ID: error %d\n", -ret);
+               ipath_free_devdata(pdev, dd);
+               dd = ERR_PTR(ret);
+               goto bail_unlock;
+       }
+
+       dd->pcidev = pdev;
+       pci_set_drvdata(pdev, dd);
+
+       list_add(&dd->ipath_list, &ipath_dev_list);
+
+bail_unlock:
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+
+bail:
+       return dd;
+}
+
+static inline struct ipath_devdata *__ipath_lookup(int unit)
+{
+       return idr_find(&unit_table, unit);
+}
+
+struct ipath_devdata *ipath_lookup(int unit)
+{
+       struct ipath_devdata *dd;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+       dd = __ipath_lookup(unit);
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+
+       return dd;
+}
+
+int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp)
+{
+       int nunits, npresent, nup;
+       struct ipath_devdata *dd;
+       unsigned long flags;
+       u32 maxports;
+
+       nunits = npresent = nup = maxports = 0;
+
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+
+       list_for_each_entry(dd, &ipath_dev_list, ipath_list) {
+               nunits++;
+               if ((dd->ipath_flags & IPATH_PRESENT) && dd->ipath_kregbase)
+                       npresent++;
+               if (dd->ipath_lid &&
+                   !(dd->ipath_flags & (IPATH_DISABLED | IPATH_LINKDOWN
+                                        | IPATH_LINKUNK)))
+                       nup++;
+               if (dd->ipath_cfgports > maxports)
+                       maxports = dd->ipath_cfgports;
+       }
+
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+
+       if (npresentp)
+               *npresentp = npresent;
+       if (nupp)
+               *nupp = nup;
+       if (maxportsp)
+               *maxportsp = maxports;
+
+       return nunits;
+}
+
+static int init_port0_rcvhdrtail(struct pci_dev *pdev)
+{
+       int ret;
+
+       mutex_lock(&ipath_mutex);
+
+       if (!ipath_port0_rcvhdrtail) {
+               ipath_port0_rcvhdrtail =
+                       dma_alloc_coherent(&pdev->dev,
+                                          IPATH_PORT0_RCVHDRTAIL_SIZE,
+                                          &ipath_port0_rcvhdrtail_dma,
+                                          GFP_KERNEL);
+
+               if (!ipath_port0_rcvhdrtail) {
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+       }
+       port0_rcvhdrtail_refs++;
+       ret = 0;
+
+bail:
+       mutex_unlock(&ipath_mutex);
+
+       return ret;
+}
+
+static void cleanup_port0_rcvhdrtail(struct pci_dev *pdev)
+{
+       mutex_lock(&ipath_mutex);
+
+       if (!--port0_rcvhdrtail_refs) {
+               dma_free_coherent(&pdev->dev, IPATH_PORT0_RCVHDRTAIL_SIZE,
+                                 (void *) ipath_port0_rcvhdrtail,
+                                 ipath_port0_rcvhdrtail_dma);
+               ipath_port0_rcvhdrtail = NULL;
+       }
+
+       mutex_unlock(&ipath_mutex);
+}
+
+/*
+ * These next two routines are placeholders in case we don't have per-arch
+ * code for controlling write combining.  If explicit control of write
+ * combining is not available, performance will probably be awful.
+ */
+
+int __attribute__((weak)) ipath_enable_wc(struct ipath_devdata *dd)
+{
+       return -EOPNOTSUPP;
+}
+
+void __attribute__((weak)) ipath_disable_wc(struct ipath_devdata *dd)
+{
+}
+
+static int __devinit ipath_init_one(struct pci_dev *pdev,
+                                   const struct pci_device_id *ent)
+{
+       int ret, len, j;
+       struct ipath_devdata *dd;
+       unsigned long long addr;
+       u32 bar0 = 0, bar1 = 0;
+       u8 rev;
+
+       ret = init_port0_rcvhdrtail(pdev);
+       if (ret < 0) {
+               printk(KERN_ERR IPATH_DRV_NAME
+                      ": Could not allocate port0_rcvhdrtail: error %d\n",
+                      -ret);
+               goto bail;
+       }
+
+       dd = ipath_alloc_devdata(pdev);
+       if (IS_ERR(dd)) {
+               ret = PTR_ERR(dd);
+               printk(KERN_ERR IPATH_DRV_NAME
+                      ": Could not allocate devdata: error %d\n", -ret);
+               goto bail_rcvhdrtail;
+       }
+
+       ipath_cdbg(VERBOSE, "initializing unit #%u\n", dd->ipath_unit);
+
+       read_bars(dd, pdev, &bar0, &bar1);
+
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               /* This can happen iff:
+                *
+                * We did a chip reset, and then failed to reprogram the
+                * BAR, or the chip reset due to an internal error.  We then
+                * unloaded the driver and reloaded it.
+                *
+                * Both reset cases set the BAR back to initial state.  For
+                * the latter case, the AER sticky error bit at offset 0x718
+                * should be set, but the Linux kernel doesn't yet know
+                * about that, it appears.  If the original BAR was retained
+                * in the kernel data structures, this may be OK.
+                */
+               ipath_dev_err(dd, "enable unit %d failed: error %d\n",
+                             dd->ipath_unit, -ret);
+               goto bail_devdata;
+       }
+       addr = pci_resource_start(pdev, 0);
+       len = pci_resource_len(pdev, 0);
+       ipath_cdbg(VERBOSE, "regbase (0) %llx len %d irq %x, vend %x/%x "
+                  "driver_data %lx\n", addr, len, pdev->irq, ent->vendor,
+                  ent->device, ent->driver_data);
+
+       read_bars(dd, pdev, &bar0, &bar1);
+
+       if (!bar1 && !(bar0 & ~0xf)) {
+               if (addr) {
+                       dev_info(&pdev->dev, "BAR is 0 (probable RESET), "
+                                "rewriting as %llx\n", addr);
+                       ret = pci_write_config_dword(
+                               pdev, PCI_BASE_ADDRESS_0, addr);
+                       if (ret) {
+                               ipath_dev_err(dd, "rewrite of BAR0 "
+                                             "failed: err %d\n", -ret);
+                               goto bail_disable;
+                       }
+                       ret = pci_write_config_dword(
+                               pdev, PCI_BASE_ADDRESS_1, addr >> 32);
+                       if (ret) {
+                               ipath_dev_err(dd, "rewrite of BAR1 "
+                                             "failed: err %d\n", -ret);
+                               goto bail_disable;
+                       }
+               } else {
+                       ipath_dev_err(dd, "BAR is 0 (probable RESET), "
+                                     "not usable until reboot\n");
+                       ret = -ENODEV;
+                       goto bail_disable;
+               }
+       }
+
+       ret = pci_request_regions(pdev, IPATH_DRV_NAME);
+       if (ret) {
+               dev_info(&pdev->dev, "pci_request_regions unit %u fails: "
+                        "err %d\n", dd->ipath_unit, -ret);
+               goto bail_disable;
+       }
+
+       ret = pci_set_dma_mask(pdev, DMA_64BIT_MASK);
+       if (ret) {
+               dev_info(&pdev->dev, "pci_set_dma_mask unit %u "
+                        "fails: %d\n", dd->ipath_unit, ret);
+               goto bail_regions;
+       }
+
+       pci_set_master(pdev);
+
+       /*
+        * Save BARs to rewrite after device reset.  Save all 64 bits of
+        * BAR, just in case.
+        */
+       dd->ipath_pcibar0 = addr;
+       dd->ipath_pcibar1 = addr >> 32;
+       dd->ipath_deviceid = ent->device;       /* save for later use */
+       dd->ipath_vendorid = ent->vendor;
+
+       /* setup the chip-specific functions, as early as possible. */
+       switch (ent->device) {
+       case PCI_DEVICE_ID_INFINIPATH_HT:
+               ipath_init_ht400_funcs(dd);
+               break;
+       case PCI_DEVICE_ID_INFINIPATH_PE800:
+               ipath_init_pe800_funcs(dd);
+               break;
+       default:
+               ipath_dev_err(dd, "Found unknown PathScale deviceid 0x%x, "
+                             "failing\n", ent->device);
+               return -ENODEV;
+       }
+
+       for (j = 0; j < 6; j++) {
+               if (!pdev->resource[j].start)
+                       continue;
+               ipath_cdbg(VERBOSE, "BAR %d start %lx, end %lx, len %lx\n",
+                          j, pdev->resource[j].start,
+                          pdev->resource[j].end,
+                          pci_resource_len(pdev, j));
+       }
+
+       if (!addr) {
+               ipath_dev_err(dd, "No valid address in BAR 0!\n");
+               ret = -ENODEV;
+               goto bail_regions;
+       }
+
+       dd->ipath_deviceid = ent->device;       /* save for later use */
+       dd->ipath_vendorid = ent->vendor;
+
+       ret = pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);
+       if (ret) {
+               ipath_dev_err(dd, "Failed to read PCI revision ID unit "
+                             "%u: err %d\n", dd->ipath_unit, -ret);
+               goto bail_regions;      /* shouldn't ever happen */
+       }
+       dd->ipath_pcirev = rev;
+
+       dd->ipath_kregbase = ioremap_nocache(addr, len);
+
+       if (!dd->ipath_kregbase) {
+               ipath_dbg("Unable to map io addr %llx to kvirt, failing\n",
+                         addr);
+               ret = -ENOMEM;
+               goto bail_iounmap;
+       }
+       dd->ipath_kregend = (u64 __iomem *)
+               ((void __iomem *)dd->ipath_kregbase + len);
+       dd->ipath_physaddr = addr;      /* used for io_remap, etc. */
+       /* for user mmap */
+       dd->ipath_kregvirt = (u64 __iomem *) phys_to_virt(addr);
+       ipath_cdbg(VERBOSE, "mapped io addr %llx to kregbase %p "
+                  "kregvirt %p\n", addr, dd->ipath_kregbase,
+                  dd->ipath_kregvirt);
+
+       /*
+        * clear ipath_flags here instead of in ipath_init_chip as it is set
+        * by ipath_setup_htconfig.
+        */
+       dd->ipath_flags = 0;
+
+       if (dd->ipath_f_bus(dd, pdev))
+               ipath_dev_err(dd, "Failed to setup config space; "
+                             "continuing anyway\n");
+
+       /*
+        * set up our interrupt handler; SA_SHIRQ probably not needed,
+        * since MSI interrupts shouldn't be shared but won't  hurt for now.
+        * check 0 irq after we return from chip-specific bus setup, since
+        * that can affect this due to setup
+        */
+       if (!pdev->irq)
+               ipath_dev_err(dd, "irq is 0, BIOS error?  Interrupts won't "
+                             "work\n");
+       else {
+               ret = request_irq(pdev->irq, ipath_intr, SA_SHIRQ,
+                                 IPATH_DRV_NAME, dd);
+               if (ret) {
+                       ipath_dev_err(dd, "Couldn't setup irq handler, "
+                                     "irq=%u: %d\n", pdev->irq, ret);
+                       goto bail_iounmap;
+               }
+       }
+
+       ret = ipath_init_chip(dd, 0);   /* do the chip-specific init */
+       if (ret)
+               goto bail_iounmap;
+
+       ret = ipath_enable_wc(dd);
+
+       if (ret) {
+               ipath_dev_err(dd, "Write combining not enabled "
+                             "(err %d): performance may be poor\n",
+                             -ret);
+               ret = 0;
+       }
+
+       ipath_device_create_group(&pdev->dev, dd);
+       ipathfs_add_device(dd);
+       ipath_user_add(dd);
+       ipath_layer_add(dd);
+
+       goto bail;
+
+bail_iounmap:
+       iounmap((volatile void __iomem *) dd->ipath_kregbase);
+
+bail_regions:
+       pci_release_regions(pdev);
+
+bail_disable:
+       pci_disable_device(pdev);
+
+bail_devdata:
+       ipath_free_devdata(pdev, dd);
+
+bail_rcvhdrtail:
+       cleanup_port0_rcvhdrtail(pdev);
+
+bail:
+       return ret;
+}
+
+static void __devexit ipath_remove_one(struct pci_dev *pdev)
+{
+       struct ipath_devdata *dd;
+
+       ipath_cdbg(VERBOSE, "removing, pdev=%p\n", pdev);
+       if (!pdev)
+               return;
+
+       dd = pci_get_drvdata(pdev);
+       ipath_layer_del(dd);
+       ipath_user_del(dd);
+       ipathfs_remove_device(dd);
+       ipath_device_remove_group(&pdev->dev, dd);
+       ipath_cdbg(VERBOSE, "Releasing pci memory regions, dd %p, "
+                  "unit %u\n", dd, (u32) dd->ipath_unit);
+       if (dd->ipath_kregbase) {
+               ipath_cdbg(VERBOSE, "Unmapping kregbase %p\n",
+                          dd->ipath_kregbase);
+               iounmap((volatile void __iomem *) dd->ipath_kregbase);
+               dd->ipath_kregbase = NULL;
+       }
+       pci_release_regions(pdev);
+       ipath_cdbg(VERBOSE, "calling pci_disable_device\n");
+       pci_disable_device(pdev);
+
+       ipath_free_devdata(pdev, dd);
+       cleanup_port0_rcvhdrtail(pdev);
+}
+
+/* general driver use */
+DEFINE_MUTEX(ipath_mutex);
+
+static DEFINE_SPINLOCK(ipath_pioavail_lock);
+
+/**
+ * ipath_disarm_piobufs - cancel a range of PIO buffers
+ * @dd: the infinipath device
+ * @first: the first PIO buffer to cancel
+ * @cnt: the number of PIO buffers to cancel
+ *
+ * cancel a range of PIO buffers, used when they might be armed, but
+ * not triggered.  Used at init to ensure buffer state, and also user
+ * process close, in case it died while writing to a PIO buffer
+ * Also after errors.
+ */
+void ipath_disarm_piobufs(struct ipath_devdata *dd, unsigned first,
+                         unsigned cnt)
+{
+       unsigned i, last = first + cnt;
+       u64 sendctrl, sendorig;
+
+       ipath_cdbg(PKT, "disarm %u PIObufs first=%u\n", cnt, first);
+       sendorig = dd->ipath_sendctrl | INFINIPATH_S_DISARM;
+       for (i = first; i < last; i++) {
+               sendctrl = sendorig |
+                       (i << INFINIPATH_S_DISARMPIOBUF_SHIFT);
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                                sendctrl);
+       }
+
+       /*
+        * Write it again with current value, in case ipath_sendctrl changed
+        * while we were looping; no critical bits that would require
+        * locking.
+        *
+        * Write a 0, and then the original value, reading scratch in
+        * between.  This seems to avoid a chip timing race that causes
+        * pioavail updates to memory to stop.
+        */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                        0);
+       sendorig = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                        dd->ipath_sendctrl);
+}
+
+/**
+ * ipath_wait_linkstate - wait for an IB link state change to occur
+ * @dd: the infinipath device
+ * @state: the state to wait for
+ * @msecs: the number of milliseconds to wait
+ *
+ * wait up to msecs milliseconds for IB link state change to occur for
+ * now, take the easy polling route.  Currently used only by
+ * ipath_layer_set_linkstate.  Returns 0 if state reached, otherwise
+ * -ETIMEDOUT state can have multiple states set, for any of several
+ * transitions.
+ */
+int ipath_wait_linkstate(struct ipath_devdata *dd, u32 state, int msecs)
+{
+       dd->ipath_sma_state_wanted = state;
+       wait_event_interruptible_timeout(ipath_sma_state_wait,
+                                        (dd->ipath_flags & state),
+                                        msecs_to_jiffies(msecs));
+       dd->ipath_sma_state_wanted = 0;
+
+       if (!(dd->ipath_flags & state)) {
+               u64 val;
+               ipath_cdbg(SMA, "Didn't reach linkstate %s within %u ms\n",
+                          /* test INIT ahead of DOWN, both can be set */
+                          (state & IPATH_LINKINIT) ? "INIT" :
+                          ((state & IPATH_LINKDOWN) ? "DOWN" :
+                           ((state & IPATH_LINKARMED) ? "ARM" : "ACTIVE")),
+                          msecs);
+               val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_ibcstatus);
+               ipath_cdbg(VERBOSE, "ibcc=%llx ibcstatus=%llx (%s)\n",
+                          (unsigned long long) ipath_read_kreg64(
+                                  dd, dd->ipath_kregs->kr_ibcctrl),
+                          (unsigned long long) val,
+                          ipath_ibcstatus_str[val & 0xf]);
+       }
+       return (dd->ipath_flags & state) ? 0 : -ETIMEDOUT;
+}
+
+void ipath_decode_err(char *buf, size_t blen, ipath_err_t err)
+{
+       *buf = '\0';
+       if (err & INFINIPATH_E_RHDRLEN)
+               strlcat(buf, "rhdrlen ", blen);
+       if (err & INFINIPATH_E_RBADTID)
+               strlcat(buf, "rbadtid ", blen);
+       if (err & INFINIPATH_E_RBADVERSION)
+               strlcat(buf, "rbadversion ", blen);
+       if (err & INFINIPATH_E_RHDR)
+               strlcat(buf, "rhdr ", blen);
+       if (err & INFINIPATH_E_RLONGPKTLEN)
+               strlcat(buf, "rlongpktlen ", blen);
+       if (err & INFINIPATH_E_RSHORTPKTLEN)
+               strlcat(buf, "rshortpktlen ", blen);
+       if (err & INFINIPATH_E_RMAXPKTLEN)
+               strlcat(buf, "rmaxpktlen ", blen);
+       if (err & INFINIPATH_E_RMINPKTLEN)
+               strlcat(buf, "rminpktlen ", blen);
+       if (err & INFINIPATH_E_RFORMATERR)
+               strlcat(buf, "rformaterr ", blen);
+       if (err & INFINIPATH_E_RUNSUPVL)
+               strlcat(buf, "runsupvl ", blen);
+       if (err & INFINIPATH_E_RUNEXPCHAR)
+               strlcat(buf, "runexpchar ", blen);
+       if (err & INFINIPATH_E_RIBFLOW)
+               strlcat(buf, "ribflow ", blen);
+       if (err & INFINIPATH_E_REBP)
+               strlcat(buf, "EBP ", blen);
+       if (err & INFINIPATH_E_SUNDERRUN)
+               strlcat(buf, "sunderrun ", blen);
+       if (err & INFINIPATH_E_SPIOARMLAUNCH)
+               strlcat(buf, "spioarmlaunch ", blen);
+       if (err & INFINIPATH_E_SUNEXPERRPKTNUM)
+               strlcat(buf, "sunexperrpktnum ", blen);
+       if (err & INFINIPATH_E_SDROPPEDDATAPKT)
+               strlcat(buf, "sdroppeddatapkt ", blen);
+       if (err & INFINIPATH_E_SDROPPEDSMPPKT)
+               strlcat(buf, "sdroppedsmppkt ", blen);
+       if (err & INFINIPATH_E_SMAXPKTLEN)
+               strlcat(buf, "smaxpktlen ", blen);
+       if (err & INFINIPATH_E_SMINPKTLEN)
+               strlcat(buf, "sminpktlen ", blen);
+       if (err & INFINIPATH_E_SUNSUPVL)
+               strlcat(buf, "sunsupVL ", blen);
+       if (err & INFINIPATH_E_SPKTLEN)
+               strlcat(buf, "spktlen ", blen);
+       if (err & INFINIPATH_E_INVALIDADDR)
+               strlcat(buf, "invalidaddr ", blen);
+       if (err & INFINIPATH_E_RICRC)
+               strlcat(buf, "CRC ", blen);
+       if (err & INFINIPATH_E_RVCRC)
+               strlcat(buf, "VCRC ", blen);
+       if (err & INFINIPATH_E_RRCVEGRFULL)
+               strlcat(buf, "rcvegrfull ", blen);
+       if (err & INFINIPATH_E_RRCVHDRFULL)
+               strlcat(buf, "rcvhdrfull ", blen);
+       if (err & INFINIPATH_E_IBSTATUSCHANGED)
+               strlcat(buf, "ibcstatuschg ", blen);
+       if (err & INFINIPATH_E_RIBLOSTLINK)
+               strlcat(buf, "riblostlink ", blen);
+       if (err & INFINIPATH_E_HARDWARE)
+               strlcat(buf, "hardware ", blen);
+       if (err & INFINIPATH_E_RESET)
+               strlcat(buf, "reset ", blen);
+}
+
+/**
+ * get_rhf_errstring - decode RHF errors
+ * @err: the err number
+ * @msg: the output buffer
+ * @len: the length of the output buffer
+ *
+ * only used one place now, may want more later
+ */
+static void get_rhf_errstring(u32 err, char *msg, size_t len)
+{
+       /* if no errors, and so don't need to check what's first */
+       *msg = '\0';
+
+       if (err & INFINIPATH_RHF_H_ICRCERR)
+               strlcat(msg, "icrcerr ", len);
+       if (err & INFINIPATH_RHF_H_VCRCERR)
+               strlcat(msg, "vcrcerr ", len);
+       if (err & INFINIPATH_RHF_H_PARITYERR)
+               strlcat(msg, "parityerr ", len);
+       if (err & INFINIPATH_RHF_H_LENERR)
+               strlcat(msg, "lenerr ", len);
+       if (err & INFINIPATH_RHF_H_MTUERR)
+               strlcat(msg, "mtuerr ", len);
+       if (err & INFINIPATH_RHF_H_IHDRERR)
+               /* infinipath hdr checksum error */
+               strlcat(msg, "ipathhdrerr ", len);
+       if (err & INFINIPATH_RHF_H_TIDERR)
+               strlcat(msg, "tiderr ", len);
+       if (err & INFINIPATH_RHF_H_MKERR)
+               /* bad port, offset, etc. */
+               strlcat(msg, "invalid ipathhdr ", len);
+       if (err & INFINIPATH_RHF_H_IBERR)
+               strlcat(msg, "iberr ", len);
+       if (err & INFINIPATH_RHF_L_SWA)
+               strlcat(msg, "swA ", len);
+       if (err & INFINIPATH_RHF_L_SWB)
+               strlcat(msg, "swB ", len);
+}
+
+/**
+ * ipath_get_egrbuf - get an eager buffer
+ * @dd: the infinipath device
+ * @bufnum: the eager buffer to get
+ * @err: unused
+ *
+ * must only be called if ipath_pd[port] is known to be allocated
+ */
+static inline void *ipath_get_egrbuf(struct ipath_devdata *dd, u32 bufnum,
+                                    int err)
+{
+       return dd->ipath_port0_skbs ?
+               (void *)dd->ipath_port0_skbs[bufnum]->data : NULL;
+}
+
+/**
+ * ipath_alloc_skb - allocate an skb and buffer with possible constraints
+ * @dd: the infinipath device
+ * @gfp_mask: the sk_buff SFP mask
+ */
+struct sk_buff *ipath_alloc_skb(struct ipath_devdata *dd,
+                               gfp_t gfp_mask)
+{
+       struct sk_buff *skb;
+       u32 len;
+
+       /*
+        * Only fully supported way to handle this is to allocate lots
+        * extra, align as needed, and then do skb_reserve().  That wastes
+        * a lot of memory...  I'll have to hack this into infinipath_copy
+        * also.
+        */
+
+       /*
+        * We need 4 extra bytes for unaligned transfer copying
+        */
+       if (dd->ipath_flags & IPATH_4BYTE_TID) {
+               /* we need a 4KB multiple alignment, and there is no way
+                * to do it except to allocate extra and then skb_reserve
+                * enough to bring it up to the right alignment.
+                */
+               len = dd->ipath_ibmaxlen + 4 + (1 << 11) - 1;
+       }
+       else
+               len = dd->ipath_ibmaxlen + 4;
+       skb = __dev_alloc_skb(len, gfp_mask);
+       if (!skb) {
+               ipath_dev_err(dd, "Failed to allocate skbuff, length %u\n",
+                             len);
+               goto bail;
+       }
+       if (dd->ipath_flags & IPATH_4BYTE_TID) {
+               u32 una = ((1 << 11) - 1) & (unsigned long)(skb->data + 4);
+               if (una)
+                       skb_reserve(skb, 4 + (1 << 11) - una);
+               else
+                       skb_reserve(skb, 4);
+       } else
+               skb_reserve(skb, 4);
+
+bail:
+       return skb;
+}
+
+/**
+ * ipath_rcv_layer - receive a packet for the layered (ethernet) driver
+ * @dd: the infinipath device
+ * @etail: the sk_buff number
+ * @tlen: the total packet length
+ * @hdr: the ethernet header
+ *
+ * Separate routine for better overall optimization
+ */
+static void ipath_rcv_layer(struct ipath_devdata *dd, u32 etail,
+                           u32 tlen, struct ether_header *hdr)
+{
+       u32 elen;
+       u8 pad, *bthbytes;
+       struct sk_buff *skb, *nskb;
+
+       if (dd->ipath_port0_skbs && hdr->sub_opcode == OPCODE_ENCAP) {
+               /*
+                * Allocate a new sk_buff to replace the one we give
+                * to the network stack.
+                */
+               nskb = ipath_alloc_skb(dd, GFP_ATOMIC);
+               if (!nskb) {
+                       /* count OK packets that we drop */
+                       ipath_stats.sps_krdrops++;
+                       return;
+               }
+
+               bthbytes = (u8 *) hdr->bth;
+               pad = (bthbytes[1] >> 4) & 3;
+               /* +CRC32 */
+               elen = tlen - (sizeof(*hdr) + pad + sizeof(u32));
+
+               skb = dd->ipath_port0_skbs[etail];
+               dd->ipath_port0_skbs[etail] = nskb;
+               skb_put(skb, elen);
+
+               dd->ipath_f_put_tid(dd, etail + (u64 __iomem *)
+                                   ((char __iomem *) dd->ipath_kregbase
+                                    + dd->ipath_rcvegrbase), 0,
+                                   virt_to_phys(nskb->data));
+
+               __ipath_layer_rcv(dd, hdr, skb);
+
+               /* another ether packet received */
+               ipath_stats.sps_ether_rpkts++;
+       }
+       else if (hdr->sub_opcode == OPCODE_LID_ARP)
+               __ipath_layer_rcv_lid(dd, hdr);
+}
+
+/*
+ * ipath_kreceive - receive a packet
+ * @dd: the infinipath device
+ *
+ * called from interrupt handler for errors or receive interrupt
+ */
+void ipath_kreceive(struct ipath_devdata *dd)
+{
+       u64 *rc;
+       void *ebuf;
+       const u32 rsize = dd->ipath_rcvhdrentsize;      /* words */
+       const u32 maxcnt = dd->ipath_rcvhdrcnt * rsize; /* words */
+       u32 etail = -1, l, hdrqtail;
+       struct ips_message_header *hdr;
+       u32 eflags, i, etype, tlen, pkttot = 0;
+       static u64 totcalls;    /* stats, may eventually remove */
+       char emsg[128];
+
+       if (!dd->ipath_hdrqtailptr) {
+               ipath_dev_err(dd,
+                             "hdrqtailptr not set, can't do receives\n");
+               goto bail;
+       }
+
+       /* There is already a thread processing this queue. */
+       if (test_and_set_bit(0, &dd->ipath_rcv_pending))
+               goto bail;
+
+       if (dd->ipath_port0head ==
+           (u32)le64_to_cpu(*dd->ipath_hdrqtailptr))
+               goto done;
+
+gotmore:
+       /*
+        * read only once at start.  If in flood situation, this helps
+        * performance slightly.  If more arrive while we are processing,
+        * we'll come back here and do them
+        */
+       hdrqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
+
+       for (i = 0, l = dd->ipath_port0head; l != hdrqtail; i++) {
+               u32 qp;
+               u8 *bthbytes;
+
+               rc = (u64 *) (dd->ipath_pd[0]->port_rcvhdrq + (l << 2));
+               hdr = (struct ips_message_header *)&rc[1];
+               /*
+                * could make a network order version of IPATH_KD_QP, and
+                * do the obvious shift before masking to speed this up.
+                */
+               qp = ntohl(hdr->bth[1]) & 0xffffff;
+               bthbytes = (u8 *) hdr->bth;
+
+               eflags = ips_get_hdr_err_flags((__le32 *) rc);
+               etype = ips_get_rcv_type((__le32 *) rc);
+               /* total length */
+               tlen = ips_get_length_in_bytes((__le32 *) rc);
+               ebuf = NULL;
+               if (etype != RCVHQ_RCV_TYPE_EXPECTED) {
+                       /*
+                        * it turns out that the chips uses an eager buffer
+                        * for all non-expected packets, whether it "needs"
+                        * one or not.  So always get the index, but don't
+                        * set ebuf (so we try to copy data) unless the
+                        * length requires it.
+                        */
+                       etail = ips_get_index((__le32 *) rc);
+                       if (tlen > sizeof(*hdr) ||
+                           etype == RCVHQ_RCV_TYPE_NON_KD)
+                               ebuf = ipath_get_egrbuf(dd, etail, 0);
+               }
+
+               /*
+                * both tiderr and ipathhdrerr are set for all plain IB
+                * packets; only ipathhdrerr should be set.
+                */
+
+               if (etype != RCVHQ_RCV_TYPE_NON_KD && etype !=
+                   RCVHQ_RCV_TYPE_ERROR && ips_get_ipath_ver(
+                           hdr->iph.ver_port_tid_offset) !=
+                   IPS_PROTO_VERSION) {
+                       ipath_cdbg(PKT, "Bad InfiniPath protocol version "
+                                  "%x\n", etype);
+               }
+
+               if (eflags & ~(INFINIPATH_RHF_H_TIDERR |
+                              INFINIPATH_RHF_H_IHDRERR)) {
+                       get_rhf_errstring(eflags, emsg, sizeof emsg);
+                       ipath_cdbg(PKT, "RHFerrs %x hdrqtail=%x typ=%u "
+                                  "tlen=%x opcode=%x egridx=%x: %s\n",
+                                  eflags, l, etype, tlen, bthbytes[0],
+                                  ips_get_index((__le32 *) rc), emsg);
+               } else if (etype == RCVHQ_RCV_TYPE_NON_KD) {
+                               int ret = __ipath_verbs_rcv(dd, rc + 1,
+                                                           ebuf, tlen);
+                               if (ret == -ENODEV)
+                                       ipath_cdbg(VERBOSE,
+                                                  "received IB packet, "
+                                                  "not SMA (QP=%x)\n", qp);
+               } else if (etype == RCVHQ_RCV_TYPE_EAGER) {
+                       if (qp == IPATH_KD_QP &&
+                           bthbytes[0] == ipath_layer_rcv_opcode &&
+                           ebuf)
+                               ipath_rcv_layer(dd, etail, tlen,
+                                               (struct ether_header *)hdr);
+                       else
+                               ipath_cdbg(PKT, "typ %x, opcode %x (eager, "
+                                          "qp=%x), len %x; ignored\n",
+                                          etype, bthbytes[0], qp, tlen);
+               }
+               else if (etype == RCVHQ_RCV_TYPE_EXPECTED)
+                       ipath_dbg("Bug: Expected TID, opcode %x; ignored\n",
+                                 be32_to_cpu(hdr->bth[0]) & 0xff);
+               else if (eflags & (INFINIPATH_RHF_H_TIDERR |
+                                  INFINIPATH_RHF_H_IHDRERR)) {
+                       /*
+                        * This is a type 3 packet, only the LRH is in the
+                        * rcvhdrq, the rest of the header is in the eager
+                        * buffer.
+                        */
+                       u8 opcode;
+                       if (ebuf) {
+                               bthbytes = (u8 *) ebuf;
+                               opcode = *bthbytes;
+                       }
+                       else
+                               opcode = 0;
+                       get_rhf_errstring(eflags, emsg, sizeof emsg);
+                       ipath_dbg("Err %x (%s), opcode %x, egrbuf %x, "
+                                 "len %x\n", eflags, emsg, opcode, etail,
+                                 tlen);
+               } else {
+                       /*
+                        * error packet, type of error  unknown.
+                        * Probably type 3, but we don't know, so don't
+                        * even try to print the opcode, etc.
+                        */
+                       ipath_dbg("Error Pkt, but no eflags! egrbuf %x, "
+                                 "len %x\nhdrq@%lx;hdrq+%x rhf: %llx; "
+                                 "hdr %llx %llx %llx %llx %llx\n",
+                                 etail, tlen, (unsigned long) rc, l,
+                                 (unsigned long long) rc[0],
+                                 (unsigned long long) rc[1],
+                                 (unsigned long long) rc[2],
+                                 (unsigned long long) rc[3],
+                                 (unsigned long long) rc[4],
+                                 (unsigned long long) rc[5]);
+               }
+               l += rsize;
+               if (l >= maxcnt)
+                       l = 0;
+               /*
+                * update for each packet, to help prevent overflows if we
+                * have lots of packets.
+                */
+               (void)ipath_write_ureg(dd, ur_rcvhdrhead,
+                                      dd->ipath_rhdrhead_intr_off | l, 0);
+               if (etype != RCVHQ_RCV_TYPE_EXPECTED)
+                       (void)ipath_write_ureg(dd, ur_rcvegrindexhead,
+                                              etail, 0);
+       }
+
+       pkttot += i;
+
+       dd->ipath_port0head = l;
+
+       if (hdrqtail != (u32)le64_to_cpu(*dd->ipath_hdrqtailptr))
+               /* more arrived while we handled first batch */
+               goto gotmore;
+
+       if (pkttot > ipath_stats.sps_maxpkts_call)
+               ipath_stats.sps_maxpkts_call = pkttot;
+       ipath_stats.sps_port0pkts += pkttot;
+       ipath_stats.sps_avgpkts_call =
+               ipath_stats.sps_port0pkts / ++totcalls;
+
+done:
+       clear_bit(0, &dd->ipath_rcv_pending);
+       smp_mb__after_clear_bit();
+
+bail:;
+}
+
+/**
+ * ipath_update_pio_bufs - update shadow copy of the PIO availability map
+ * @dd: the infinipath device
+ *
+ * called whenever our local copy indicates we have run out of send buffers
+ * NOTE: This can be called from interrupt context by some code
+ * and from non-interrupt context by ipath_getpiobuf().
+ */
+
+static void ipath_update_pio_bufs(struct ipath_devdata *dd)
+{
+       unsigned long flags;
+       int i;
+       const unsigned piobregs = (unsigned)dd->ipath_pioavregs;
+
+       /* If the generation (check) bits have changed, then we update the
+        * busy bit for the corresponding PIO buffer.  This algorithm will
+        * modify positions to the value they already have in some cases
+        * (i.e., no change), but it's faster than changing only the bits
+        * that have changed.
+        *
+        * We would like to do this atomicly, to avoid spinlocks in the
+        * critical send path, but that's not really possible, given the
+        * type of changes, and that this routine could be called on
+        * multiple cpu's simultaneously, so we lock in this routine only,
+        * to avoid conflicting updates; all we change is the shadow, and
+        * it's a single 64 bit memory location, so by definition the update
+        * is atomic in terms of what other cpu's can see in testing the
+        * bits.  The spin_lock overhead isn't too bad, since it only
+        * happens when all buffers are in use, so only cpu overhead, not
+        * latency or bandwidth is affected.
+        */
+#define _IPATH_ALL_CHECKBITS 0x5555555555555555ULL
+       if (!dd->ipath_pioavailregs_dma) {
+               ipath_dbg("Update shadow pioavail, but regs_dma NULL!\n");
+               return;
+       }
+       if (ipath_debug & __IPATH_VERBDBG) {
+               /* only if packet debug and verbose */
+               volatile __le64 *dma = dd->ipath_pioavailregs_dma;
+               unsigned long *shadow = dd->ipath_pioavailshadow;
+
+               ipath_cdbg(PKT, "Refill avail, dma0=%llx shad0=%lx, "
+                          "d1=%llx s1=%lx, d2=%llx s2=%lx, d3=%llx "
+                          "s3=%lx\n",
+                          (unsigned long long) le64_to_cpu(dma[0]),
+                          shadow[0],
+                          (unsigned long long) le64_to_cpu(dma[1]),
+                          shadow[1],
+                          (unsigned long long) le64_to_cpu(dma[2]),
+                          shadow[2],
+                          (unsigned long long) le64_to_cpu(dma[3]),
+                          shadow[3]);
+               if (piobregs > 4)
+                       ipath_cdbg(
+                               PKT, "2nd group, dma4=%llx shad4=%lx, "
+                               "d5=%llx s5=%lx, d6=%llx s6=%lx, "
+                               "d7=%llx s7=%lx\n",
+                               (unsigned long long) le64_to_cpu(dma[4]),
+                               shadow[4],
+                               (unsigned long long) le64_to_cpu(dma[5]),
+                               shadow[5],
+                               (unsigned long long) le64_to_cpu(dma[6]),
+                               shadow[6],
+                               (unsigned long long) le64_to_cpu(dma[7]),
+                               shadow[7]);
+       }
+       spin_lock_irqsave(&ipath_pioavail_lock, flags);
+       for (i = 0; i < piobregs; i++) {
+               u64 pchbusy, pchg, piov, pnew;
+               /*
+                * Chip Errata: bug 6641; even and odd qwords>3 are swapped
+                */
+               if (i > 3) {
+                       if (i & 1)
+                               piov = le64_to_cpu(
+                                       dd->ipath_pioavailregs_dma[i - 1]);
+                       else
+                               piov = le64_to_cpu(
+                                       dd->ipath_pioavailregs_dma[i + 1]);
+               } else
+                       piov = le64_to_cpu(dd->ipath_pioavailregs_dma[i]);
+               pchg = _IPATH_ALL_CHECKBITS &
+                       ~(dd->ipath_pioavailshadow[i] ^ piov);
+               pchbusy = pchg << INFINIPATH_SENDPIOAVAIL_BUSY_SHIFT;
+               if (pchg && (pchbusy & dd->ipath_pioavailshadow[i])) {
+                       pnew = dd->ipath_pioavailshadow[i] & ~pchbusy;
+                       pnew |= piov & pchbusy;
+                       dd->ipath_pioavailshadow[i] = pnew;
+               }
+       }
+       spin_unlock_irqrestore(&ipath_pioavail_lock, flags);
+}
+
+/**
+ * ipath_setrcvhdrsize - set the receive header size
+ * @dd: the infinipath device
+ * @rhdrsize: the receive header size
+ *
+ * called from user init code, and also layered driver init
+ */
+int ipath_setrcvhdrsize(struct ipath_devdata *dd, unsigned rhdrsize)
+{
+       int ret = 0;
+
+       if (dd->ipath_flags & IPATH_RCVHDRSZ_SET) {
+               if (dd->ipath_rcvhdrsize != rhdrsize) {
+                       dev_info(&dd->pcidev->dev,
+                                "Error: can't set protocol header "
+                                "size %u, already %u\n",
+                                rhdrsize, dd->ipath_rcvhdrsize);
+                       ret = -EAGAIN;
+               } else
+                       ipath_cdbg(VERBOSE, "Reuse same protocol header "
+                                  "size %u\n", dd->ipath_rcvhdrsize);
+       } else if (rhdrsize > (dd->ipath_rcvhdrentsize -
+                              (sizeof(u64) / sizeof(u32)))) {
+               ipath_dbg("Error: can't set protocol header size %u "
+                         "(> max %u)\n", rhdrsize,
+                         dd->ipath_rcvhdrentsize -
+                         (u32) (sizeof(u64) / sizeof(u32)));
+               ret = -EOVERFLOW;
+       } else {
+               dd->ipath_flags |= IPATH_RCVHDRSZ_SET;
+               dd->ipath_rcvhdrsize = rhdrsize;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvhdrsize,
+                                dd->ipath_rcvhdrsize);
+               ipath_cdbg(VERBOSE, "Set protocol header size to %u\n",
+                          dd->ipath_rcvhdrsize);
+       }
+       return ret;
+}
+
+/**
+ * ipath_getpiobuf - find an available pio buffer
+ * @dd: the infinipath device
+ * @pbufnum: the buffer number is placed here
+ *
+ * do appropriate marking as busy, etc.
+ * returns buffer number if one found (>=0), negative number is error.
+ * Used by ipath_sma_send_pkt and ipath_layer_send
+ */
+u32 __iomem *ipath_getpiobuf(struct ipath_devdata *dd, u32 * pbufnum)
+{
+       int i, j, starti, updated = 0;
+       unsigned piobcnt, iter;
+       unsigned long flags;
+       unsigned long *shadow = dd->ipath_pioavailshadow;
+       u32 __iomem *buf;
+
+       piobcnt = (unsigned)(dd->ipath_piobcnt2k
+                            + dd->ipath_piobcnt4k);
+       starti = dd->ipath_lastport_piobuf;
+       iter = piobcnt - starti;
+       if (dd->ipath_upd_pio_shadow) {
+               /*
+                * Minor optimization.  If we had no buffers on last call,
+                * start out by doing the update; continue and do scan even
+                * if no buffers were updated, to be paranoid
+                */
+               ipath_update_pio_bufs(dd);
+               /* we scanned here, don't do it at end of scan */
+               updated = 1;
+               i = starti;
+       } else
+               i = dd->ipath_lastpioindex;
+
+rescan:
+       /*
+        * while test_and_set_bit() is atomic, we do that and then the
+        * change_bit(), and the pair is not.  See if this is the cause
+        * of the remaining armlaunch errors.
+        */
+       spin_lock_irqsave(&ipath_pioavail_lock, flags);
+       for (j = 0; j < iter; j++, i++) {
+               if (i >= piobcnt)
+                       i = starti;
+               /*
+                * To avoid bus lock overhead, we first find a candidate
+                * buffer, then do the test and set, and continue if that
+                * fails.
+                */
+               if (test_bit((2 * i) + 1, shadow) ||
+                   test_and_set_bit((2 * i) + 1, shadow))
+                       continue;
+               /* flip generation bit */
+               change_bit(2 * i, shadow);
+               break;
+       }
+       spin_unlock_irqrestore(&ipath_pioavail_lock, flags);
+
+       if (j == iter) {
+               volatile __le64 *dma = dd->ipath_pioavailregs_dma;
+
+               /*
+                * first time through; shadow exhausted, but may be real
+                * buffers available, so go see; if any updated, rescan
+                * (once)
+                */
+               if (!updated) {
+                       ipath_update_pio_bufs(dd);
+                       updated = 1;
+                       i = starti;
+                       goto rescan;
+               }
+               dd->ipath_upd_pio_shadow = 1;
+               /*
+                * not atomic, but if we lose one once in a while, that's OK
+                */
+               ipath_stats.sps_nopiobufs++;
+               if (!(++dd->ipath_consec_nopiobuf % 100000)) {
+                       ipath_dbg(
+                               "%u pio sends with no bufavail; dmacopy: "
+                               "%llx %llx %llx %llx; shadow:  "
+                               "%lx %lx %lx %lx\n",
+                               dd->ipath_consec_nopiobuf,
+                               (unsigned long long) le64_to_cpu(dma[0]),
+                               (unsigned long long) le64_to_cpu(dma[1]),
+                               (unsigned long long) le64_to_cpu(dma[2]),
+                               (unsigned long long) le64_to_cpu(dma[3]),
+                               shadow[0], shadow[1], shadow[2],
+                               shadow[3]);
+                       /*
+                        * 4 buffers per byte, 4 registers above, cover rest
+                        * below
+                        */
+                       if ((dd->ipath_piobcnt2k + dd->ipath_piobcnt4k) >
+                           (sizeof(shadow[0]) * 4 * 4))
+                               ipath_dbg("2nd group: dmacopy: %llx %llx "
+                                         "%llx %llx; shadow: %lx %lx "
+                                         "%lx %lx\n",
+                                         (unsigned long long)
+                                         le64_to_cpu(dma[4]),
+                                         (unsigned long long)
+                                         le64_to_cpu(dma[5]),
+                                         (unsigned long long)
+                                         le64_to_cpu(dma[6]),
+                                         (unsigned long long)
+                                         le64_to_cpu(dma[7]),
+                                         shadow[4], shadow[5],
+                                         shadow[6], shadow[7]);
+               }
+               buf = NULL;
+               goto bail;
+       }
+
+       if (updated)
+               /*
+                * ran out of bufs, now some (at least this one we just
+                * got) are now available, so tell the layered driver.
+                */
+               __ipath_layer_intr(dd, IPATH_LAYER_INT_SEND_CONTINUE);
+
+       /*
+        * set next starting place.  Since it's just an optimization,
+        * it doesn't matter who wins on this, so no locking
+        */
+       dd->ipath_lastpioindex = i + 1;
+       if (dd->ipath_upd_pio_shadow)
+               dd->ipath_upd_pio_shadow = 0;
+       if (dd->ipath_consec_nopiobuf)
+               dd->ipath_consec_nopiobuf = 0;
+       if (i < dd->ipath_piobcnt2k)
+               buf = (u32 __iomem *) (dd->ipath_pio2kbase +
+                                      i * dd->ipath_palign);
+       else
+               buf = (u32 __iomem *)
+                       (dd->ipath_pio4kbase +
+                        (i - dd->ipath_piobcnt2k) * dd->ipath_4kalign);
+       ipath_cdbg(VERBOSE, "Return piobuf%u %uk @ %p\n",
+                  i, (i < dd->ipath_piobcnt2k) ? 2 : 4, buf);
+       if (pbufnum)
+               *pbufnum = i;
+
+bail:
+       return buf;
+}
+
+/**
+ * ipath_create_rcvhdrq - create a receive header queue
+ * @dd: the infinipath device
+ * @pd: the port data
+ *
+ * this *must* be physically contiguous memory, and for now,
+ * that limits it to what kmalloc can do.
+ */
+int ipath_create_rcvhdrq(struct ipath_devdata *dd,
+                        struct ipath_portdata *pd)
+{
+       int ret = 0, amt;
+
+       amt = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize *
+                   sizeof(u32), PAGE_SIZE);
+       if (!pd->port_rcvhdrq) {
+               /*
+                * not using REPEAT isn't viable; at 128KB, we can easily
+                * fail this.  The problem with REPEAT is we can block here
+                * "forever".  There isn't an inbetween, unfortunately.  We
+                * could reduce the risk by never freeing the rcvhdrq except
+                * at unload, but even then, the first time a port is used,
+                * we could delay for some time...
+                */
+               gfp_t gfp_flags = GFP_USER | __GFP_COMP;
+
+               pd->port_rcvhdrq = dma_alloc_coherent(
+                       &dd->pcidev->dev, amt, &pd->port_rcvhdrq_phys,
+                       gfp_flags);
+
+               if (!pd->port_rcvhdrq) {
+                       ipath_dev_err(dd, "attempt to allocate %d bytes "
+                                     "for port %u rcvhdrq failed\n",
+                                     amt, pd->port_port);
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+
+               pd->port_rcvhdrq_size = amt;
+
+               ipath_cdbg(VERBOSE, "%d pages at %p (phys %lx) size=%lu "
+                          "for port %u rcvhdr Q\n",
+                          amt >> PAGE_SHIFT, pd->port_rcvhdrq,
+                          (unsigned long) pd->port_rcvhdrq_phys,
+                          (unsigned long) pd->port_rcvhdrq_size,
+                          pd->port_port);
+       } else {
+               /*
+                * clear for security, sanity, and/or debugging, each
+                * time we reuse
+                */
+               memset(pd->port_rcvhdrq, 0, amt);
+       }
+
+       /*
+        * tell chip each time we init it, even if we are re-using previous
+        * memory (we zero it at process close)
+        */
+       ipath_cdbg(VERBOSE, "writing port %d rcvhdraddr as %lx\n",
+                  pd->port_port, (unsigned long) pd->port_rcvhdrq_phys);
+       ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdraddr,
+                             pd->port_port, pd->port_rcvhdrq_phys);
+
+       ret = 0;
+bail:
+       return ret;
+}
+
+int ipath_waitfor_complete(struct ipath_devdata *dd, ipath_kreg reg_id,
+                          u64 bits_to_wait_for, u64 * valp)
+{
+       unsigned long timeout;
+       u64 lastval, val;
+       int ret;
+
+       lastval = ipath_read_kreg64(dd, reg_id);
+       /* wait a ridiculously long time */
+       timeout = jiffies + msecs_to_jiffies(5);
+       do {
+               val = ipath_read_kreg64(dd, reg_id);
+               /* set so they have something, even on failures. */
+               *valp = val;
+               if ((val & bits_to_wait_for) == bits_to_wait_for) {
+                       ret = 0;
+                       break;
+               }
+               if (val != lastval)
+                       ipath_cdbg(VERBOSE, "Changed from %llx to %llx, "
+                                  "waiting for %llx bits\n",
+                                  (unsigned long long) lastval,
+                                  (unsigned long long) val,
+                                  (unsigned long long) bits_to_wait_for);
+               cond_resched();
+               if (time_after(jiffies, timeout)) {
+                       ipath_dbg("Didn't get bits %llx in register 0x%x, "
+                                 "got %llx\n",
+                                 (unsigned long long) bits_to_wait_for,
+                                 reg_id, (unsigned long long) *valp);
+                       ret = -ENODEV;
+                       break;
+               }
+       } while (1);
+
+       return ret;
+}
+
+/**
+ * ipath_waitfor_mdio_cmdready - wait for last command to complete
+ * @dd: the infinipath device
+ *
+ * Like ipath_waitfor_complete(), but we wait for the CMDVALID bit to go
+ * away indicating the last command has completed.  It doesn't return data
+ */
+int ipath_waitfor_mdio_cmdready(struct ipath_devdata *dd)
+{
+       unsigned long timeout;
+       u64 val;
+       int ret;
+
+       /* wait a ridiculously long time */
+       timeout = jiffies + msecs_to_jiffies(5);
+       do {
+               val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_mdio);
+               if (!(val & IPATH_MDIO_CMDVALID)) {
+                       ret = 0;
+                       break;
+               }
+               cond_resched();
+               if (time_after(jiffies, timeout)) {
+                       ipath_dbg("CMDVALID stuck in mdio reg? (%llx)\n",
+                                 (unsigned long long) val);
+                       ret = -ENODEV;
+                       break;
+               }
+       } while (1);
+
+       return ret;
+}
+
+void ipath_set_ib_lstate(struct ipath_devdata *dd, int which)
+{
+       static const char *what[4] = {
+               [0] = "DOWN",
+               [INFINIPATH_IBCC_LINKCMD_INIT] = "INIT",
+               [INFINIPATH_IBCC_LINKCMD_ARMED] = "ARMED",
+               [INFINIPATH_IBCC_LINKCMD_ACTIVE] = "ACTIVE"
+       };
+       ipath_cdbg(SMA, "Trying to move unit %u to %s, current ltstate "
+                  "is %s\n", dd->ipath_unit,
+                  what[(which >> INFINIPATH_IBCC_LINKCMD_SHIFT) &
+                       INFINIPATH_IBCC_LINKCMD_MASK],
+                  ipath_ibcstatus_str[
+                          (ipath_read_kreg64
+                           (dd, dd->ipath_kregs->kr_ibcstatus) >>
+                           INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) &
+                          INFINIPATH_IBCS_LINKTRAININGSTATE_MASK]);
+
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl,
+                        dd->ipath_ibcctrl | which);
+}
+
+/**
+ * ipath_read_kreg64_port - read a device's per-port 64-bit kernel register
+ * @dd: the infinipath device
+ * @regno: the register number to read
+ * @port: the port containing the register
+ *
+ * Registers that vary with the chip implementation constants (port)
+ * use this routine.
+ */
+u64 ipath_read_kreg64_port(const struct ipath_devdata *dd, ipath_kreg regno,
+                          unsigned port)
+{
+       u16 where;
+
+       if (port < dd->ipath_portcnt &&
+           (regno == dd->ipath_kregs->kr_rcvhdraddr ||
+            regno == dd->ipath_kregs->kr_rcvhdrtailaddr))
+               where = regno + port;
+       else
+               where = -1;
+
+       return ipath_read_kreg64(dd, where);
+}
+
+/**
+ * ipath_write_kreg_port - write a device's per-port 64-bit kernel register
+ * @dd: the infinipath device
+ * @regno: the register number to write
+ * @port: the port containing the register
+ * @value: the value to write
+ *
+ * Registers that vary with the chip implementation constants (port)
+ * use this routine.
+ */
+void ipath_write_kreg_port(const struct ipath_devdata *dd, ipath_kreg regno,
+                         unsigned port, u64 value)
+{
+       u16 where;
+
+       if (port < dd->ipath_portcnt &&
+           (regno == dd->ipath_kregs->kr_rcvhdraddr ||
+            regno == dd->ipath_kregs->kr_rcvhdrtailaddr))
+               where = regno + port;
+       else
+               where = -1;
+
+       ipath_write_kreg(dd, where, value);
+}
+
+/**
+ * ipath_shutdown_device - shut down a device
+ * @dd: the infinipath device
+ *
+ * This is called to make the device quiet when we are about to
+ * unload the driver, and also when the device is administratively
+ * disabled.   It does not free any data structures.
+ * Everything it does has to be setup again by ipath_init_chip(dd,1)
+ */
+void ipath_shutdown_device(struct ipath_devdata *dd)
+{
+       u64 val;
+
+       ipath_dbg("Shutting down the device\n");
+
+       dd->ipath_flags |= IPATH_LINKUNK;
+       dd->ipath_flags &= ~(IPATH_INITTED | IPATH_LINKDOWN |
+                            IPATH_LINKINIT | IPATH_LINKARMED |
+                            IPATH_LINKACTIVE);
+       *dd->ipath_statusp &= ~(IPATH_STATUS_IB_CONF |
+                               IPATH_STATUS_IB_READY);
+
+       /* mask interrupts, but not errors */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask, 0ULL);
+
+       dd->ipath_rcvctrl = 0;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+                        dd->ipath_rcvctrl);
+
+       /*
+        * gracefully stop all sends allowing any in progress to trickle out
+        * first.
+        */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, 0ULL);
+       /* flush it */
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+       /*
+        * enough for anything that's going to trickle out to have actually
+        * done so.
+        */
+       udelay(5);
+
+       /*
+        * abort any armed or launched PIO buffers that didn't go. (self
+        * clearing).  Will cause any packet currently being transmitted to
+        * go out with an EBP, and may also cause a short packet error on
+        * the receiver.
+        */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                        INFINIPATH_S_ABORT);
+
+       ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKINITCMD_DISABLE <<
+                           INFINIPATH_IBCC_LINKINITCMD_SHIFT);
+
+       /*
+        * we are shutting down, so tell the layered driver.  We don't do
+        * this on just a link state change, much like ethernet, a cable
+        * unplug, etc. doesn't change driver state
+        */
+       ipath_layer_intr(dd, IPATH_LAYER_INT_IF_DOWN);
+
+       /* disable IBC */
+       dd->ipath_control &= ~INFINIPATH_C_LINKENABLE;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
+                        dd->ipath_control);
+
+       /*
+        * clear SerdesEnable and turn the leds off; do this here because
+        * we are unloading, so don't count on interrupts to move along
+        * Turn the LEDs off explictly for the same reason.
+        */
+       dd->ipath_f_quiet_serdes(dd);
+       dd->ipath_f_setextled(dd, 0, 0);
+
+       if (dd->ipath_stats_timer_active) {
+               del_timer_sync(&dd->ipath_stats_timer);
+               dd->ipath_stats_timer_active = 0;
+       }
+
+       /*
+        * clear all interrupts and errors, so that the next time the driver
+        * is loaded or device is enabled, we know that whatever is set
+        * happened while we were unloaded
+        */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear,
+                        ~0ULL & ~INFINIPATH_HWE_MEMBISTFAILED);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, -1LL);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, -1LL);
+}
+
+/**
+ * ipath_free_pddata - free a port's allocated data
+ * @dd: the infinipath device
+ * @port: the port
+ * @freehdrq: free the port data structure if true
+ *
+ * when closing, free up any allocated data for a port, if the
+ * reference count goes to zero
+ * Note: this also optionally frees the portdata itself!
+ * Any changes here have to be matched up with the reinit case
+ * of ipath_init_chip(), which calls this routine on reinit after reset.
+ */
+void ipath_free_pddata(struct ipath_devdata *dd, u32 port, int freehdrq)
+{
+       struct ipath_portdata *pd = dd->ipath_pd[port];
+
+       if (!pd)
+               return;
+       if (freehdrq)
+               /*
+                * only clear and free portdata if we are going to also
+                * release the hdrq, otherwise we leak the hdrq on each
+                * open/close cycle
+                */
+               dd->ipath_pd[port] = NULL;
+       if (freehdrq && pd->port_rcvhdrq) {
+               ipath_cdbg(VERBOSE, "free closed port %d rcvhdrq @ %p "
+                          "(size=%lu)\n", pd->port_port, pd->port_rcvhdrq,
+                          (unsigned long) pd->port_rcvhdrq_size);
+               dma_free_coherent(&dd->pcidev->dev, pd->port_rcvhdrq_size,
+                                 pd->port_rcvhdrq, pd->port_rcvhdrq_phys);
+               pd->port_rcvhdrq = NULL;
+       }
+       if (port && pd->port_rcvegrbuf) {
+               /* always free this */
+               if (pd->port_rcvegrbuf) {
+                       unsigned e;
+
+                       for (e = 0; e < pd->port_rcvegrbuf_chunks; e++) {
+                               void *base = pd->port_rcvegrbuf[e];
+                               size_t size = pd->port_rcvegrbuf_size;
+
+                               ipath_cdbg(VERBOSE, "egrbuf free(%p, %lu), "
+                                          "chunk %u/%u\n", base,
+                                          (unsigned long) size,
+                                          e, pd->port_rcvegrbuf_chunks);
+                               dma_free_coherent(
+                                       &dd->pcidev->dev, size, base,
+                                       pd->port_rcvegrbuf_phys[e]);
+                       }
+                       vfree(pd->port_rcvegrbuf);
+                       pd->port_rcvegrbuf = NULL;
+                       vfree(pd->port_rcvegrbuf_phys);
+                       pd->port_rcvegrbuf_phys = NULL;
+               }
+               pd->port_rcvegrbuf_chunks = 0;
+       } else if (port == 0 && dd->ipath_port0_skbs) {
+               unsigned e;
+               struct sk_buff **skbs = dd->ipath_port0_skbs;
+
+               dd->ipath_port0_skbs = NULL;
+               ipath_cdbg(VERBOSE, "free closed port %d ipath_port0_skbs "
+                          "@ %p\n", pd->port_port, skbs);
+               for (e = 0; e < dd->ipath_rcvegrcnt; e++)
+                       if (skbs[e])
+                               dev_kfree_skb(skbs[e]);
+               vfree(skbs);
+       }
+       if (freehdrq) {
+               kfree(pd->port_tid_pg_list);
+               kfree(pd);
+       }
+}
+
+int __init infinipath_init(void)
+{
+       int ret;
+
+       ipath_dbg(KERN_INFO DRIVER_LOAD_MSG "%s", ipath_core_version);
+
+       /*
+        * These must be called before the driver is registered with
+        * the PCI subsystem.
+        */
+       idr_init(&unit_table);
+       if (!idr_pre_get(&unit_table, GFP_KERNEL)) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+
+       ret = pci_register_driver(&ipath_driver);
+       if (ret < 0) {
+               printk(KERN_ERR IPATH_DRV_NAME
+                      ": Unable to register driver: error %d\n", -ret);
+               goto bail_unit;
+       }
+
+       ret = ipath_driver_create_group(&ipath_driver.driver);
+       if (ret < 0) {
+               printk(KERN_ERR IPATH_DRV_NAME ": Unable to create driver "
+                      "sysfs entries: error %d\n", -ret);
+               goto bail_pci;
+       }
+
+       ret = ipath_init_ipathfs();
+       if (ret < 0) {
+               printk(KERN_ERR IPATH_DRV_NAME ": Unable to create "
+                      "ipathfs: error %d\n", -ret);
+               goto bail_group;
+       }
+
+       goto bail;
+
+bail_group:
+       ipath_driver_remove_group(&ipath_driver.driver);
+
+bail_pci:
+       pci_unregister_driver(&ipath_driver);
+
+bail_unit:
+       idr_destroy(&unit_table);
+
+bail:
+       return ret;
+}
+
+static void cleanup_device(struct ipath_devdata *dd)
+{
+       int port;
+
+       ipath_shutdown_device(dd);
+
+       if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) {
+               /* can't do anything more with chip; needs re-init */
+               *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT;
+               if (dd->ipath_kregbase) {
+                       /*
+                        * if we haven't already cleaned up before these are
+                        * to ensure any register reads/writes "fail" until
+                        * re-init
+                        */
+                       dd->ipath_kregbase = NULL;
+                       dd->ipath_kregvirt = NULL;
+                       dd->ipath_uregbase = 0;
+                       dd->ipath_sregbase = 0;
+                       dd->ipath_cregbase = 0;
+                       dd->ipath_kregsize = 0;
+               }
+               ipath_disable_wc(dd);
+       }
+
+       if (dd->ipath_pioavailregs_dma) {
+               dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE,
+                                 (void *) dd->ipath_pioavailregs_dma,
+                                 dd->ipath_pioavailregs_phys);
+               dd->ipath_pioavailregs_dma = NULL;
+       }
+
+       if (dd->ipath_pageshadow) {
+               struct page **tmpp = dd->ipath_pageshadow;
+               int i, cnt = 0;
+
+               ipath_cdbg(VERBOSE, "Unlocking any expTID pages still "
+                          "locked\n");
+               for (port = 0; port < dd->ipath_cfgports; port++) {
+                       int port_tidbase = port * dd->ipath_rcvtidcnt;
+                       int maxtid = port_tidbase + dd->ipath_rcvtidcnt;
+                       for (i = port_tidbase; i < maxtid; i++) {
+                               if (!tmpp[i])
+                                       continue;
+                               ipath_release_user_pages(&tmpp[i], 1);
+                               tmpp[i] = NULL;
+                               cnt++;
+                       }
+               }
+               if (cnt) {
+                       ipath_stats.sps_pageunlocks += cnt;
+                       ipath_cdbg(VERBOSE, "There were still %u expTID "
+                                  "entries locked\n", cnt);
+               }
+               if (ipath_stats.sps_pagelocks ||
+                   ipath_stats.sps_pageunlocks)
+                       ipath_cdbg(VERBOSE, "%llu pages locked, %llu "
+                                  "unlocked via ipath_m{un}lock\n",
+                                  (unsigned long long)
+                                  ipath_stats.sps_pagelocks,
+                                  (unsigned long long)
+                                  ipath_stats.sps_pageunlocks);
+
+               ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n",
+                          dd->ipath_pageshadow);
+               vfree(dd->ipath_pageshadow);
+               dd->ipath_pageshadow = NULL;
+       }
+
+       /*
+        * free any resources still in use (usually just kernel ports)
+        * at unload
+        */
+       for (port = 0; port < dd->ipath_cfgports; port++)
+               ipath_free_pddata(dd, port, 1);
+       kfree(dd->ipath_pd);
+       /*
+        * debuggability, in case some cleanup path tries to use it
+        * after this
+        */
+       dd->ipath_pd = NULL;
+}
+
+static void __exit infinipath_cleanup(void)
+{
+       struct ipath_devdata *dd, *tmp;
+       unsigned long flags;
+
+       ipath_exit_ipathfs();
+
+       ipath_driver_remove_group(&ipath_driver.driver);
+
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+
+       /*
+        * turn off rcv, send, and interrupts for all ports, all drivers
+        * should also hard reset the chip here?
+        * free up port 0 (kernel) rcvhdr, egr bufs, and eventually tid bufs
+        * for all versions of the driver, if they were allocated
+        */
+       list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) {
+               spin_unlock_irqrestore(&ipath_devs_lock, flags);
+
+               if (dd->ipath_kregbase)
+                       cleanup_device(dd);
+
+               if (dd->pcidev) {
+                       if (dd->pcidev->irq) {
+                               ipath_cdbg(VERBOSE,
+                                          "unit %u free_irq of irq %x\n",
+                                          dd->ipath_unit, dd->pcidev->irq);
+                               free_irq(dd->pcidev->irq, dd);
+                       } else
+                               ipath_dbg("irq is 0, not doing free_irq "
+                                         "for unit %u\n", dd->ipath_unit);
+                       dd->pcidev = NULL;
+               }
+
+               /*
+                * we check for NULL here, because it's outside the kregbase
+                * check, and we need to call it after the free_irq.  Thus
+                * it's possible that the function pointers were never
+                * initialized.
+                */
+               if (dd->ipath_f_cleanup)
+                       /* clean up chip-specific stuff */
+                       dd->ipath_f_cleanup(dd);
+
+               spin_lock_irqsave(&ipath_devs_lock, flags);
+       }
+
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+
+       ipath_cdbg(VERBOSE, "Unregistering pci driver\n");
+       pci_unregister_driver(&ipath_driver);
+
+       idr_destroy(&unit_table);
+}
+
+/**
+ * ipath_reset_device - reset the chip if possible
+ * @unit: the device to reset
+ *
+ * Whether or not reset is successful, we attempt to re-initialize the chip
+ * (that is, much like a driver unload/reload).  We clear the INITTED flag
+ * so that the various entry points will fail until we reinitialize.  For
+ * now, we only allow this if no user ports are open that use chip resources
+ */
+int ipath_reset_device(int unit)
+{
+       int ret, i;
+       struct ipath_devdata *dd = ipath_lookup(unit);
+
+       if (!dd) {
+               ret = -ENODEV;
+               goto bail;
+       }
+
+       dev_info(&dd->pcidev->dev, "Reset on unit %u requested\n", unit);
+
+       if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT)) {
+               dev_info(&dd->pcidev->dev, "Invalid unit number %u or "
+                        "not initialized or not present\n", unit);
+               ret = -ENXIO;
+               goto bail;
+       }
+
+       if (dd->ipath_pd)
+               for (i = 1; i < dd->ipath_portcnt; i++) {
+                       if (dd->ipath_pd[i] && dd->ipath_pd[i]->port_cnt) {
+                               ipath_dbg("unit %u port %d is in use "
+                                         "(PID %u cmd %s), can't reset\n",
+                                         unit, i,
+                                         dd->ipath_pd[i]->port_pid,
+                                         dd->ipath_pd[i]->port_comm);
+                               ret = -EBUSY;
+                               goto bail;
+                       }
+               }
+
+       dd->ipath_flags &= ~IPATH_INITTED;
+       ret = dd->ipath_f_reset(dd);
+       if (ret != 1)
+               ipath_dbg("reset was not successful\n");
+       ipath_dbg("Trying to reinitialize unit %u after reset attempt\n",
+                 unit);
+       ret = ipath_init_chip(dd, 1);
+       if (ret)
+               ipath_dev_err(dd, "Reinitialize unit %u after "
+                             "reset failed with %d\n", unit, ret);
+       else
+               dev_info(&dd->pcidev->dev, "Reinitialized unit %u after "
+                        "resetting\n", unit);
+
+bail:
+       return ret;
+}
+
+module_init(infinipath_init);
+module_exit(infinipath_cleanup);
diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c
new file mode 100644 (file)
index 0000000..f11a900
--- /dev/null
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+
+#include "ipath_kernel.h"
+
+/*
+ * InfiniPath I2C driver for a serial eeprom.  This is not a generic
+ * I2C interface.  For a start, the device we're using (Atmel AT24C11)
+ * doesn't work like a regular I2C device.  It looks like one
+ * electrically, but not logically.  Normal I2C devices have a single
+ * 7-bit or 10-bit I2C address that they respond to.  Valid 7-bit
+ * addresses range from 0x03 to 0x77.  Addresses 0x00 to 0x02 and 0x78
+ * to 0x7F are special reserved addresses (e.g. 0x00 is the "general
+ * call" address.)  The Atmel device, on the other hand, responds to ALL
+ * 7-bit addresses.  It's designed to be the only device on a given I2C
+ * bus.  A 7-bit address corresponds to the memory address within the
+ * Atmel device itself.
+ *
+ * Also, the timing requirements mean more than simple software
+ * bitbanging, with readbacks from chip to ensure timing (simple udelay
+ * is not enough).
+ *
+ * This all means that accessing the device is specialized enough
+ * that using the standard kernel I2C bitbanging interface would be
+ * impossible.  For example, the core I2C eeprom driver expects to find
+ * a device at one or more of a limited set of addresses only.  It doesn't
+ * allow writing to an eeprom.  It also doesn't provide any means of
+ * accessing eeprom contents from within the kernel, only via sysfs.
+ */
+
+enum i2c_type {
+       i2c_line_scl = 0,
+       i2c_line_sda
+};
+
+enum i2c_state {
+       i2c_line_low = 0,
+       i2c_line_high
+};
+
+#define READ_CMD 1
+#define WRITE_CMD 0
+
+static int eeprom_init;
+
+/*
+ * The gpioval manipulation really should be protected by spinlocks
+ * or be converted to use atomic operations.
+ */
+
+/**
+ * i2c_gpio_set - set a GPIO line
+ * @dd: the infinipath device
+ * @line: the line to set
+ * @new_line_state: the state to set
+ *
+ * Returns 0 if the line was set to the new state successfully, non-zero
+ * on error.
+ */
+static int i2c_gpio_set(struct ipath_devdata *dd,
+                       enum i2c_type line,
+                       enum i2c_state new_line_state)
+{
+       u64 read_val, write_val, mask, *gpioval;
+
+       gpioval = &dd->ipath_gpio_out;
+       read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl);
+       if (line == i2c_line_scl)
+               mask = ipath_gpio_scl;
+       else
+               mask = ipath_gpio_sda;
+
+       if (new_line_state == i2c_line_high)
+               /* tri-state the output rather than force high */
+               write_val = read_val & ~mask;
+       else
+               /* config line to be an output */
+               write_val = read_val | mask;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, write_val);
+
+       /* set high and verify */
+       if (new_line_state == i2c_line_high)
+               write_val = 0x1UL;
+       else
+               write_val = 0x0UL;
+
+       if (line == i2c_line_scl) {
+               write_val <<= ipath_gpio_scl_num;
+               *gpioval = *gpioval & ~(1UL << ipath_gpio_scl_num);
+               *gpioval |= write_val;
+       } else {
+               write_val <<= ipath_gpio_sda_num;
+               *gpioval = *gpioval & ~(1UL << ipath_gpio_sda_num);
+               *gpioval |= write_val;
+       }
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_out, *gpioval);
+
+       return 0;
+}
+
+/**
+ * i2c_gpio_get - get a GPIO line state
+ * @dd: the infinipath device
+ * @line: the line to get
+ * @curr_statep: where to put the line state
+ *
+ * Returns 0 if the line was set to the new state successfully, non-zero
+ * on error.  curr_state is not set on error.
+ */
+static int i2c_gpio_get(struct ipath_devdata *dd,
+                       enum i2c_type line,
+                       enum i2c_state *curr_statep)
+{
+       u64 read_val, write_val, mask;
+       int ret;
+
+       /* check args */
+       if (curr_statep == NULL) {
+               ret = 1;
+               goto bail;
+       }
+
+       read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl);
+       /* config line to be an input */
+       if (line == i2c_line_scl)
+               mask = ipath_gpio_scl;
+       else
+               mask = ipath_gpio_sda;
+       write_val = read_val & ~mask;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, write_val);
+       read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus);
+
+       if (read_val & mask)
+               *curr_statep = i2c_line_high;
+       else
+               *curr_statep = i2c_line_low;
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+/**
+ * i2c_wait_for_writes - wait for a write
+ * @dd: the infinipath device
+ *
+ * We use this instead of udelay directly, so we can make sure
+ * that previous register writes have been flushed all the way
+ * to the chip.  Since we are delaying anyway, the cost doesn't
+ * hurt, and makes the bit twiddling more regular
+ */
+static void i2c_wait_for_writes(struct ipath_devdata *dd)
+{
+       (void)ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch);
+}
+
+static void scl_out(struct ipath_devdata *dd, u8 bit)
+{
+       i2c_gpio_set(dd, i2c_line_scl, bit ? i2c_line_high : i2c_line_low);
+
+       i2c_wait_for_writes(dd);
+}
+
+static void sda_out(struct ipath_devdata *dd, u8 bit)
+{
+       i2c_gpio_set(dd, i2c_line_sda, bit ? i2c_line_high : i2c_line_low);
+
+       i2c_wait_for_writes(dd);
+}
+
+static u8 sda_in(struct ipath_devdata *dd, int wait)
+{
+       enum i2c_state bit;
+
+       if (i2c_gpio_get(dd, i2c_line_sda, &bit))
+               ipath_dbg("get bit failed!\n");
+
+       if (wait)
+               i2c_wait_for_writes(dd);
+
+       return bit == i2c_line_high ? 1U : 0;
+}
+
+/**
+ * i2c_ackrcv - see if ack following write is true
+ * @dd: the infinipath device
+ */
+static int i2c_ackrcv(struct ipath_devdata *dd)
+{
+       u8 ack_received;
+
+       /* AT ENTRY SCL = LOW */
+       /* change direction, ignore data */
+       ack_received = sda_in(dd, 1);
+       scl_out(dd, i2c_line_high);
+       ack_received = sda_in(dd, 1) == 0;
+       scl_out(dd, i2c_line_low);
+       return ack_received;
+}
+
+/**
+ * wr_byte - write a byte, one bit at a time
+ * @dd: the infinipath device
+ * @data: the byte to write
+ *
+ * Returns 0 if we got the following ack, otherwise 1
+ */
+static int wr_byte(struct ipath_devdata *dd, u8 data)
+{
+       int bit_cntr;
+       u8 bit;
+
+       for (bit_cntr = 7; bit_cntr >= 0; bit_cntr--) {
+               bit = (data >> bit_cntr) & 1;
+               sda_out(dd, bit);
+               scl_out(dd, i2c_line_high);
+               scl_out(dd, i2c_line_low);
+       }
+       return (!i2c_ackrcv(dd)) ? 1 : 0;
+}
+
+static void send_ack(struct ipath_devdata *dd)
+{
+       sda_out(dd, i2c_line_low);
+       scl_out(dd, i2c_line_high);
+       scl_out(dd, i2c_line_low);
+       sda_out(dd, i2c_line_high);
+}
+
+/**
+ * i2c_startcmd - transmit the start condition, followed by address/cmd
+ * @dd: the infinipath device
+ * @offset_dir: direction byte
+ *
+ *      (both clock/data high, clock high, data low while clock is high)
+ */
+static int i2c_startcmd(struct ipath_devdata *dd, u8 offset_dir)
+{
+       int res;
+
+       /* issue start sequence */
+       sda_out(dd, i2c_line_high);
+       scl_out(dd, i2c_line_high);
+       sda_out(dd, i2c_line_low);
+       scl_out(dd, i2c_line_low);
+
+       /* issue length and direction byte */
+       res = wr_byte(dd, offset_dir);
+
+       if (res)
+               ipath_cdbg(VERBOSE, "No ack to complete start\n");
+
+       return res;
+}
+
+/**
+ * stop_cmd - transmit the stop condition
+ * @dd: the infinipath device
+ *
+ * (both clock/data low, clock high, data high while clock is high)
+ */
+static void stop_cmd(struct ipath_devdata *dd)
+{
+       scl_out(dd, i2c_line_low);
+       sda_out(dd, i2c_line_low);
+       scl_out(dd, i2c_line_high);
+       sda_out(dd, i2c_line_high);
+       udelay(2);
+}
+
+/**
+ * eeprom_reset - reset I2C communication
+ * @dd: the infinipath device
+ */
+
+static int eeprom_reset(struct ipath_devdata *dd)
+{
+       int clock_cycles_left = 9;
+       u64 *gpioval = &dd->ipath_gpio_out;
+       int ret;
+
+       eeprom_init = 1;
+       *gpioval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_out);
+       ipath_cdbg(VERBOSE, "Resetting i2c eeprom; initial gpioout reg "
+                  "is %llx\n", (unsigned long long) *gpioval);
+
+       /*
+        * This is to get the i2c into a known state, by first going low,
+        * then tristate sda (and then tristate scl as first thing
+        * in loop)
+        */
+       scl_out(dd, i2c_line_low);
+       sda_out(dd, i2c_line_high);
+
+       while (clock_cycles_left--) {
+               scl_out(dd, i2c_line_high);
+
+               if (sda_in(dd, 0)) {
+                       sda_out(dd, i2c_line_low);
+                       scl_out(dd, i2c_line_low);
+                       ret = 0;
+                       goto bail;
+               }
+
+               scl_out(dd, i2c_line_low);
+       }
+
+       ret = 1;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_eeprom_read - receives bytes from the eeprom via I2C
+ * @dd: the infinipath device
+ * @eeprom_offset: address to read from
+ * @buffer: where to store result
+ * @len: number of bytes to receive
+ */
+
+int ipath_eeprom_read(struct ipath_devdata *dd, u8 eeprom_offset,
+                     void *buffer, int len)
+{
+       /* compiler complains unless initialized */
+       u8 single_byte = 0;
+       int bit_cntr;
+       int ret;
+
+       if (!eeprom_init)
+               eeprom_reset(dd);
+
+       eeprom_offset = (eeprom_offset << 1) | READ_CMD;
+
+       if (i2c_startcmd(dd, eeprom_offset)) {
+               ipath_dbg("Failed startcmd\n");
+               stop_cmd(dd);
+               ret = 1;
+               goto bail;
+       }
+
+       /*
+        * eeprom keeps clocking data out as long as we ack, automatically
+        * incrementing the address.
+        */
+       while (len-- > 0) {
+               /* get data */
+               single_byte = 0;
+               for (bit_cntr = 8; bit_cntr; bit_cntr--) {
+                       u8 bit;
+                       scl_out(dd, i2c_line_high);
+                       bit = sda_in(dd, 0);
+                       single_byte |= bit << (bit_cntr - 1);
+                       scl_out(dd, i2c_line_low);
+               }
+
+               /* send ack if not the last byte */
+               if (len)
+                       send_ack(dd);
+
+               *((u8 *) buffer) = single_byte;
+               buffer++;
+       }
+
+       stop_cmd(dd);
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_eeprom_write - writes data to the eeprom via I2C
+ * @dd: the infinipath device
+ * @eeprom_offset: where to place data
+ * @buffer: data to write
+ * @len: number of bytes to write
+ */
+int ipath_eeprom_write(struct ipath_devdata *dd, u8 eeprom_offset,
+                      const void *buffer, int len)
+{
+       u8 single_byte;
+       int sub_len;
+       const u8 *bp = buffer;
+       int max_wait_time, i;
+       int ret;
+
+       if (!eeprom_init)
+               eeprom_reset(dd);
+
+       while (len > 0) {
+               if (i2c_startcmd(dd, (eeprom_offset << 1) | WRITE_CMD)) {
+                       ipath_dbg("Failed to start cmd offset %u\n",
+                                 eeprom_offset);
+                       goto failed_write;
+               }
+
+               sub_len = min(len, 4);
+               eeprom_offset += sub_len;
+               len -= sub_len;
+
+               for (i = 0; i < sub_len; i++) {
+                       if (wr_byte(dd, *bp++)) {
+                               ipath_dbg("no ack after byte %u/%u (%u "
+                                         "total remain)\n", i, sub_len,
+                                         len + sub_len - i);
+                               goto failed_write;
+                       }
+               }
+
+               stop_cmd(dd);
+
+               /*
+                * wait for write complete by waiting for a successful
+                * read (the chip replies with a zero after the write
+                * cmd completes, and before it writes to the eeprom.
+                * The startcmd for the read will fail the ack until
+                * the writes have completed.   We do this inline to avoid
+                * the debug prints that are in the real read routine
+                * if the startcmd fails.
+                */
+               max_wait_time = 100;
+               while (i2c_startcmd(dd, READ_CMD)) {
+                       stop_cmd(dd);
+                       if (!--max_wait_time) {
+                               ipath_dbg("Did not get successful read to "
+                                         "complete write\n");
+                               goto failed_write;
+                       }
+               }
+               /* now read the zero byte */
+               for (i = single_byte = 0; i < 8; i++) {
+                       u8 bit;
+                       scl_out(dd, i2c_line_high);
+                       bit = sda_in(dd, 0);
+                       scl_out(dd, i2c_line_low);
+                       single_byte <<= 1;
+                       single_byte |= bit;
+               }
+               stop_cmd(dd);
+       }
+
+       ret = 0;
+       goto bail;
+
+failed_write:
+       stop_cmd(dd);
+       ret = 1;
+
+bail:
+       return ret;
+}
+
+static u8 flash_csum(struct ipath_flash *ifp, int adjust)
+{
+       u8 *ip = (u8 *) ifp;
+       u8 csum = 0, len;
+
+       for (len = 0; len < ifp->if_length; len++)
+               csum += *ip++;
+       csum -= ifp->if_csum;
+       csum = ~csum;
+       if (adjust)
+               ifp->if_csum = csum;
+
+       return csum;
+}
+
+/**
+ * ipath_get_guid - get the GUID from the i2c device
+ * @dd: the infinipath device
+ *
+ * When we add the multi-chip support, we will probably have to add
+ * the ability to use the number of guids field, and get the guid from
+ * the first chip's flash, to use for all of them.
+ */
+void ipath_get_guid(struct ipath_devdata *dd)
+{
+       void *buf;
+       struct ipath_flash *ifp;
+       __be64 guid;
+       int len;
+       u8 csum, *bguid;
+       int t = dd->ipath_unit;
+       struct ipath_devdata *dd0 = ipath_lookup(0);
+
+       if (t && dd0->ipath_nguid > 1 && t <= dd0->ipath_nguid) {
+               u8 *bguid, oguid;
+               dd->ipath_guid = dd0->ipath_guid;
+               bguid = (u8 *) & dd->ipath_guid;
+
+               oguid = bguid[7];
+               bguid[7] += t;
+               if (oguid > bguid[7]) {
+                       if (bguid[6] == 0xff) {
+                               if (bguid[5] == 0xff) {
+                                       ipath_dev_err(
+                                               dd,
+                                               "Can't set %s GUID from "
+                                               "base, wraps to OUI!\n",
+                                               ipath_get_unit_name(t));
+                                       dd->ipath_guid = 0;
+                                       goto bail;
+                               }
+                               bguid[5]++;
+                       }
+                       bguid[6]++;
+               }
+               dd->ipath_nguid = 1;
+
+               ipath_dbg("nguid %u, so adding %u to device 0 guid, "
+                         "for %llx\n",
+                         dd0->ipath_nguid, t,
+                         (unsigned long long) be64_to_cpu(dd->ipath_guid));
+               goto bail;
+       }
+
+       len = offsetof(struct ipath_flash, if_future);
+       buf = vmalloc(len);
+       if (!buf) {
+               ipath_dev_err(dd, "Couldn't allocate memory to read %u "
+                             "bytes from eeprom for GUID\n", len);
+               goto bail;
+       }
+
+       if (ipath_eeprom_read(dd, 0, buf, len)) {
+               ipath_dev_err(dd, "Failed reading GUID from eeprom\n");
+               goto done;
+       }
+       ifp = (struct ipath_flash *)buf;
+
+       csum = flash_csum(ifp, 0);
+       if (csum != ifp->if_csum) {
+               dev_info(&dd->pcidev->dev, "Bad I2C flash checksum: "
+                        "0x%x, not 0x%x\n", csum, ifp->if_csum);
+               goto done;
+       }
+       if (*(__be64 *) ifp->if_guid == 0ULL ||
+           *(__be64 *) ifp->if_guid == __constant_cpu_to_be64(-1LL)) {
+               ipath_dev_err(dd, "Invalid GUID %llx from flash; "
+                             "ignoring\n",
+                             *(unsigned long long *) ifp->if_guid);
+               /* don't allow GUID if all 0 or all 1's */
+               goto done;
+       }
+
+       /* complain, but allow it */
+       if (*(u64 *) ifp->if_guid == 0x100007511000000ULL)
+               dev_info(&dd->pcidev->dev, "Warning, GUID %llx is "
+                        "default, probably not correct!\n",
+                        *(unsigned long long *) ifp->if_guid);
+
+       bguid = ifp->if_guid;
+       if (!bguid[0] && !bguid[1] && !bguid[2]) {
+               /* original incorrect GUID format in flash; fix in
+                * core copy, by shifting up 2 octets; don't need to
+                * change top octet, since both it and shifted are
+                * 0.. */
+               bguid[1] = bguid[3];
+               bguid[2] = bguid[4];
+               bguid[3] = bguid[4] = 0;
+               guid = *(__be64 *) ifp->if_guid;
+               ipath_cdbg(VERBOSE, "Old GUID format in flash, top 3 zero, "
+                          "shifting 2 octets\n");
+       } else
+               guid = *(__be64 *) ifp->if_guid;
+       dd->ipath_guid = guid;
+       dd->ipath_nguid = ifp->if_numguid;
+       memcpy(dd->ipath_serial, ifp->if_serial,
+              sizeof(ifp->if_serial));
+       ipath_cdbg(VERBOSE, "Initted GUID to %llx from eeprom\n",
+                  (unsigned long long) be64_to_cpu(dd->ipath_guid));
+
+done:
+       vfree(buf);
+
+bail:;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
new file mode 100644 (file)
index 0000000..c347191
--- /dev/null
@@ -0,0 +1,1910 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/cdev.h>
+#include <linux/swap.h>
+#include <linux/vmalloc.h>
+#include <asm/pgtable.h>
+
+#include "ipath_kernel.h"
+#include "ips_common.h"
+#include "ipath_layer.h"
+
+static int ipath_open(struct inode *, struct file *);
+static int ipath_close(struct inode *, struct file *);
+static ssize_t ipath_write(struct file *, const char __user *, size_t,
+                          loff_t *);
+static unsigned int ipath_poll(struct file *, struct poll_table_struct *);
+static int ipath_mmap(struct file *, struct vm_area_struct *);
+
+static struct file_operations ipath_file_ops = {
+       .owner = THIS_MODULE,
+       .write = ipath_write,
+       .open = ipath_open,
+       .release = ipath_close,
+       .poll = ipath_poll,
+       .mmap = ipath_mmap
+};
+
+static int ipath_get_base_info(struct ipath_portdata *pd,
+                              void __user *ubase, size_t ubase_size)
+{
+       int ret = 0;
+       struct ipath_base_info *kinfo = NULL;
+       struct ipath_devdata *dd = pd->port_dd;
+
+       if (ubase_size < sizeof(*kinfo)) {
+               ipath_cdbg(PROC,
+                          "Base size %lu, need %lu (version mismatch?)\n",
+                          (unsigned long) ubase_size,
+                          (unsigned long) sizeof(*kinfo));
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       kinfo = kzalloc(sizeof(*kinfo), GFP_KERNEL);
+       if (kinfo == NULL) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+
+       ret = dd->ipath_f_get_base_info(pd, kinfo);
+       if (ret < 0)
+               goto bail;
+
+       kinfo->spi_rcvhdr_cnt = dd->ipath_rcvhdrcnt;
+       kinfo->spi_rcvhdrent_size = dd->ipath_rcvhdrentsize;
+       kinfo->spi_tidegrcnt = dd->ipath_rcvegrcnt;
+       kinfo->spi_rcv_egrbufsize = dd->ipath_rcvegrbufsize;
+       /*
+        * have to mmap whole thing
+        */
+       kinfo->spi_rcv_egrbuftotlen =
+               pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size;
+       kinfo->spi_rcv_egrperchunk = pd->port_rcvegrbufs_perchunk;
+       kinfo->spi_rcv_egrchunksize = kinfo->spi_rcv_egrbuftotlen /
+               pd->port_rcvegrbuf_chunks;
+       kinfo->spi_tidcnt = dd->ipath_rcvtidcnt;
+       /*
+        * for this use, may be ipath_cfgports summed over all chips that
+        * are are configured and present
+        */
+       kinfo->spi_nports = dd->ipath_cfgports;
+       /* unit (chip/board) our port is on */
+       kinfo->spi_unit = dd->ipath_unit;
+       /* for now, only a single page */
+       kinfo->spi_tid_maxsize = PAGE_SIZE;
+
+       /*
+        * Doing this per port, and based on the skip value, etc.  This has
+        * to be the actual buffer size, since the protocol code treats it
+        * as an array.
+        *
+        * These have to be set to user addresses in the user code via mmap.
+        * These values are used on return to user code for the mmap target
+        * addresses only.  For 32 bit, same 44 bit address problem, so use
+        * the physical address, not virtual.  Before 2.6.11, using the
+        * page_address() macro worked, but in 2.6.11, even that returns the
+        * full 64 bit address (upper bits all 1's).  So far, using the
+        * physical addresses (or chip offsets, for chip mapping) works, but
+        * no doubt some future kernel release will chang that, and we'll be
+        * on to yet another method of dealing with this
+        */
+       kinfo->spi_rcvhdr_base = (u64) pd->port_rcvhdrq_phys;
+       kinfo->spi_rcv_egrbufs = (u64) pd->port_rcvegr_phys;
+       kinfo->spi_pioavailaddr = (u64) dd->ipath_pioavailregs_phys;
+       kinfo->spi_status = (u64) kinfo->spi_pioavailaddr +
+               (void *) dd->ipath_statusp -
+               (void *) dd->ipath_pioavailregs_dma;
+       kinfo->spi_piobufbase = (u64) pd->port_piobufs;
+       kinfo->__spi_uregbase =
+               dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
+
+       kinfo->spi_pioindex = dd->ipath_pbufsport * (pd->port_port - 1);
+       kinfo->spi_piocnt = dd->ipath_pbufsport;
+       kinfo->spi_pioalign = dd->ipath_palign;
+
+       kinfo->spi_qpair = IPATH_KD_QP;
+       kinfo->spi_piosize = dd->ipath_ibmaxlen;
+       kinfo->spi_mtu = dd->ipath_ibmaxlen;    /* maxlen, not ibmtu */
+       kinfo->spi_port = pd->port_port;
+       kinfo->spi_sw_version = IPATH_USER_SWVERSION;
+       kinfo->spi_hw_version = dd->ipath_revision;
+
+       if (copy_to_user(ubase, kinfo, sizeof(*kinfo)))
+               ret = -EFAULT;
+
+bail:
+       kfree(kinfo);
+       return ret;
+}
+
+/**
+ * ipath_tid_update - update a port TID
+ * @pd: the port
+ * @ti: the TID information
+ *
+ * The new implementation as of Oct 2004 is that the driver assigns
+ * the tid and returns it to the caller.   To make it easier to
+ * catch bugs, and to reduce search time, we keep a cursor for
+ * each port, walking the shadow tid array to find one that's not
+ * in use.
+ *
+ * For now, if we can't allocate the full list, we fail, although
+ * in the long run, we'll allocate as many as we can, and the
+ * caller will deal with that by trying the remaining pages later.
+ * That means that when we fail, we have to mark the tids as not in
+ * use again, in our shadow copy.
+ *
+ * It's up to the caller to free the tids when they are done.
+ * We'll unlock the pages as they free them.
+ *
+ * Also, right now we are locking one page at a time, but since
+ * the intended use of this routine is for a single group of
+ * virtually contiguous pages, that should change to improve
+ * performance.
+ */
+static int ipath_tid_update(struct ipath_portdata *pd,
+                           const struct ipath_tid_info *ti)
+{
+       int ret = 0, ntids;
+       u32 tid, porttid, cnt, i, tidcnt;
+       u16 *tidlist;
+       struct ipath_devdata *dd = pd->port_dd;
+       u64 physaddr;
+       unsigned long vaddr;
+       u64 __iomem *tidbase;
+       unsigned long tidmap[8];
+       struct page **pagep = NULL;
+
+       if (!dd->ipath_pageshadow) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       cnt = ti->tidcnt;
+       if (!cnt) {
+               ipath_dbg("After copyin, tidcnt 0, tidlist %llx\n",
+                         (unsigned long long) ti->tidlist);
+               /*
+                * Should we treat as success?  likely a bug
+                */
+               ret = -EFAULT;
+               goto done;
+       }
+       tidcnt = dd->ipath_rcvtidcnt;
+       if (cnt >= tidcnt) {
+               /* make sure it all fits in port_tid_pg_list */
+               dev_info(&dd->pcidev->dev, "Process tried to allocate %u "
+                        "TIDs, only trying max (%u)\n", cnt, tidcnt);
+               cnt = tidcnt;
+       }
+       pagep = (struct page **)pd->port_tid_pg_list;
+       tidlist = (u16 *) (&pagep[cnt]);
+
+       memset(tidmap, 0, sizeof(tidmap));
+       tid = pd->port_tidcursor;
+       /* before decrement; chip actual # */
+       porttid = pd->port_port * tidcnt;
+       ntids = tidcnt;
+       tidbase = (u64 __iomem *) (((char __iomem *) dd->ipath_kregbase) +
+                                  dd->ipath_rcvtidbase +
+                                  porttid * sizeof(*tidbase));
+
+       ipath_cdbg(VERBOSE, "Port%u %u tids, cursor %u, tidbase %p\n",
+                  pd->port_port, cnt, tid, tidbase);
+
+       /* virtual address of first page in transfer */
+       vaddr = ti->tidvaddr;
+       if (!access_ok(VERIFY_WRITE, (void __user *) vaddr,
+                      cnt * PAGE_SIZE)) {
+               ipath_dbg("Fail vaddr %p, %u pages, !access_ok\n",
+                         (void *)vaddr, cnt);
+               ret = -EFAULT;
+               goto done;
+       }
+       ret = ipath_get_user_pages(vaddr, cnt, pagep);
+       if (ret) {
+               if (ret == -EBUSY) {
+                       ipath_dbg("Failed to lock addr %p, %u pages "
+                                 "(already locked)\n",
+                                 (void *) vaddr, cnt);
+                       /*
+                        * for now, continue, and see what happens but with
+                        * the new implementation, this should never happen,
+                        * unless perhaps the user has mpin'ed the pages
+                        * themselves (something we need to test)
+                        */
+                       ret = 0;
+               } else {
+                       dev_info(&dd->pcidev->dev,
+                                "Failed to lock addr %p, %u pages: "
+                                "errno %d\n", (void *) vaddr, cnt, -ret);
+                       goto done;
+               }
+       }
+       for (i = 0; i < cnt; i++, vaddr += PAGE_SIZE) {
+               for (; ntids--; tid++) {
+                       if (tid == tidcnt)
+                               tid = 0;
+                       if (!dd->ipath_pageshadow[porttid + tid])
+                               break;
+               }
+               if (ntids < 0) {
+                       /*
+                        * oops, wrapped all the way through their TIDs,
+                        * and didn't have enough free; see comments at
+                        * start of routine
+                        */
+                       ipath_dbg("Not enough free TIDs for %u pages "
+                                 "(index %d), failing\n", cnt, i);
+                       i--;    /* last tidlist[i] not filled in */
+                       ret = -ENOMEM;
+                       break;
+               }
+               tidlist[i] = tid;
+               ipath_cdbg(VERBOSE, "Updating idx %u to TID %u, "
+                          "vaddr %lx\n", i, tid, vaddr);
+               /* we "know" system pages and TID pages are same size */
+               dd->ipath_pageshadow[porttid + tid] = pagep[i];
+               /*
+                * don't need atomic or it's overhead
+                */
+               __set_bit(tid, tidmap);
+               physaddr = page_to_phys(pagep[i]);
+               ipath_stats.sps_pagelocks++;
+               ipath_cdbg(VERBOSE,
+                          "TID %u, vaddr %lx, physaddr %llx pgp %p\n",
+                          tid, vaddr, (unsigned long long) physaddr,
+                          pagep[i]);
+               dd->ipath_f_put_tid(dd, &tidbase[tid], 1, physaddr);
+               /*
+                * don't check this tid in ipath_portshadow, since we
+                * just filled it in; start with the next one.
+                */
+               tid++;
+       }
+
+       if (ret) {
+               u32 limit;
+       cleanup:
+               /* jump here if copy out of updated info failed... */
+               ipath_dbg("After failure (ret=%d), undo %d of %d entries\n",
+                         -ret, i, cnt);
+               /* same code that's in ipath_free_tid() */
+               limit = sizeof(tidmap) * BITS_PER_BYTE;
+               if (limit > tidcnt)
+                       /* just in case size changes in future */
+                       limit = tidcnt;
+               tid = find_first_bit((const unsigned long *)tidmap, limit);
+               for (; tid < limit; tid++) {
+                       if (!test_bit(tid, tidmap))
+                               continue;
+                       if (dd->ipath_pageshadow[porttid + tid]) {
+                               ipath_cdbg(VERBOSE, "Freeing TID %u\n",
+                                          tid);
+                               dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
+                                                   dd->ipath_tidinvalid);
+                               dd->ipath_pageshadow[porttid + tid] = NULL;
+                               ipath_stats.sps_pageunlocks++;
+                       }
+               }
+               ipath_release_user_pages(pagep, cnt);
+       } else {
+               /*
+                * Copy the updated array, with ipath_tid's filled in, back
+                * to user.  Since we did the copy in already, this "should
+                * never fail" If it does, we have to clean up...
+                */
+               if (copy_to_user((void __user *)
+                                (unsigned long) ti->tidlist,
+                                tidlist, cnt * sizeof(*tidlist))) {
+                       ret = -EFAULT;
+                       goto cleanup;
+               }
+               if (copy_to_user((void __user *) (unsigned long) ti->tidmap,
+                                tidmap, sizeof tidmap)) {
+                       ret = -EFAULT;
+                       goto cleanup;
+               }
+               if (tid == tidcnt)
+                       tid = 0;
+               pd->port_tidcursor = tid;
+       }
+
+done:
+       if (ret)
+               ipath_dbg("Failed to map %u TID pages, failing with %d\n",
+                         ti->tidcnt, -ret);
+       return ret;
+}
+
+/**
+ * ipath_tid_free - free a port TID
+ * @pd: the port
+ * @ti: the TID info
+ *
+ * right now we are unlocking one page at a time, but since
+ * the intended use of this routine is for a single group of
+ * virtually contiguous pages, that should change to improve
+ * performance.  We check that the TID is in range for this port
+ * but otherwise don't check validity; if user has an error and
+ * frees the wrong tid, it's only their own data that can thereby
+ * be corrupted.  We do check that the TID was in use, for sanity
+ * We always use our idea of the saved address, not the address that
+ * they pass in to us.
+ */
+
+static int ipath_tid_free(struct ipath_portdata *pd,
+                         const struct ipath_tid_info *ti)
+{
+       int ret = 0;
+       u32 tid, porttid, cnt, limit, tidcnt;
+       struct ipath_devdata *dd = pd->port_dd;
+       u64 __iomem *tidbase;
+       unsigned long tidmap[8];
+
+       if (!dd->ipath_pageshadow) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       if (copy_from_user(tidmap, (void __user *)(unsigned long)ti->tidmap,
+                          sizeof tidmap)) {
+               ret = -EFAULT;
+               goto done;
+       }
+
+       porttid = pd->port_port * dd->ipath_rcvtidcnt;
+       tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) +
+                                  dd->ipath_rcvtidbase +
+                                  porttid * sizeof(*tidbase));
+
+       tidcnt = dd->ipath_rcvtidcnt;
+       limit = sizeof(tidmap) * BITS_PER_BYTE;
+       if (limit > tidcnt)
+               /* just in case size changes in future */
+               limit = tidcnt;
+       tid = find_first_bit(tidmap, limit);
+       ipath_cdbg(VERBOSE, "Port%u free %u tids; first bit (max=%d) "
+                  "set is %d, porttid %u\n", pd->port_port, ti->tidcnt,
+                  limit, tid, porttid);
+       for (cnt = 0; tid < limit; tid++) {
+               /*
+                * small optimization; if we detect a run of 3 or so without
+                * any set, use find_first_bit again.  That's mainly to
+                * accelerate the case where we wrapped, so we have some at
+                * the beginning, and some at the end, and a big gap
+                * in the middle.
+                */
+               if (!test_bit(tid, tidmap))
+                       continue;
+               cnt++;
+               if (dd->ipath_pageshadow[porttid + tid]) {
+                       ipath_cdbg(VERBOSE, "PID %u freeing TID %u\n",
+                                  pd->port_pid, tid);
+                       dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
+                                           dd->ipath_tidinvalid);
+                       ipath_release_user_pages(
+                               &dd->ipath_pageshadow[porttid + tid], 1);
+                       dd->ipath_pageshadow[porttid + tid] = NULL;
+                       ipath_stats.sps_pageunlocks++;
+               } else
+                       ipath_dbg("Unused tid %u, ignoring\n", tid);
+       }
+       if (cnt != ti->tidcnt)
+               ipath_dbg("passed in tidcnt %d, only %d bits set in map\n",
+                         ti->tidcnt, cnt);
+done:
+       if (ret)
+               ipath_dbg("Failed to unmap %u TID pages, failing with %d\n",
+                         ti->tidcnt, -ret);
+       return ret;
+}
+
+/**
+ * ipath_set_part_key - set a partition key
+ * @pd: the port
+ * @key: the key
+ *
+ * We can have up to 4 active at a time (other than the default, which is
+ * always allowed).  This is somewhat tricky, since multiple ports may set
+ * the same key, so we reference count them, and clean up at exit.  All 4
+ * partition keys are packed into a single infinipath register.  It's an
+ * error for a process to set the same pkey multiple times.  We provide no
+ * mechanism to de-allocate a pkey at this time, we may eventually need to
+ * do that.  I've used the atomic operations, and no locking, and only make
+ * a single pass through what's available.  This should be more than
+ * adequate for some time. I'll think about spinlocks or the like if and as
+ * it's necessary.
+ */
+static int ipath_set_part_key(struct ipath_portdata *pd, u16 key)
+{
+       struct ipath_devdata *dd = pd->port_dd;
+       int i, any = 0, pidx = -1;
+       u16 lkey = key & 0x7FFF;
+       int ret;
+
+       if (lkey == (IPS_DEFAULT_P_KEY & 0x7FFF)) {
+               /* nothing to do; this key always valid */
+               ret = 0;
+               goto bail;
+       }
+
+       ipath_cdbg(VERBOSE, "p%u try to set pkey %hx, current keys "
+                  "%hx:%x %hx:%x %hx:%x %hx:%x\n",
+                  pd->port_port, key, dd->ipath_pkeys[0],
+                  atomic_read(&dd->ipath_pkeyrefs[0]), dd->ipath_pkeys[1],
+                  atomic_read(&dd->ipath_pkeyrefs[1]), dd->ipath_pkeys[2],
+                  atomic_read(&dd->ipath_pkeyrefs[2]), dd->ipath_pkeys[3],
+                  atomic_read(&dd->ipath_pkeyrefs[3]));
+
+       if (!lkey) {
+               ipath_cdbg(PROC, "p%u tries to set key 0, not allowed\n",
+                          pd->port_port);
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       /*
+        * Set the full membership bit, because it has to be
+        * set in the register or the packet, and it seems
+        * cleaner to set in the register than to force all
+        * callers to set it. (see bug 4331)
+        */
+       key |= 0x8000;
+
+       for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) {
+               if (!pd->port_pkeys[i] && pidx == -1)
+                       pidx = i;
+               if (pd->port_pkeys[i] == key) {
+                       ipath_cdbg(VERBOSE, "p%u tries to set same pkey "
+                                  "(%x) more than once\n",
+                                  pd->port_port, key);
+                       ret = -EEXIST;
+                       goto bail;
+               }
+       }
+       if (pidx == -1) {
+               ipath_dbg("All pkeys for port %u already in use, "
+                         "can't set %x\n", pd->port_port, key);
+               ret = -EBUSY;
+               goto bail;
+       }
+       for (any = i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) {
+               if (!dd->ipath_pkeys[i]) {
+                       any++;
+                       continue;
+               }
+               if (dd->ipath_pkeys[i] == key) {
+                       atomic_t *pkrefs = &dd->ipath_pkeyrefs[i];
+
+                       if (atomic_inc_return(pkrefs) > 1) {
+                               pd->port_pkeys[pidx] = key;
+                               ipath_cdbg(VERBOSE, "p%u set key %x "
+                                          "matches #%d, count now %d\n",
+                                          pd->port_port, key, i,
+                                          atomic_read(pkrefs));
+                               ret = 0;
+                               goto bail;
+                       } else {
+                               /*
+                                * lost race, decrement count, catch below
+                                */
+                               atomic_dec(pkrefs);
+                               ipath_cdbg(VERBOSE, "Lost race, count was "
+                                          "0, after dec, it's %d\n",
+                                          atomic_read(pkrefs));
+                               any++;
+                       }
+               }
+               if ((dd->ipath_pkeys[i] & 0x7FFF) == lkey) {
+                       /*
+                        * It makes no sense to have both the limited and
+                        * full membership PKEY set at the same time since
+                        * the unlimited one will disable the limited one.
+                        */
+                       ret = -EEXIST;
+                       goto bail;
+               }
+       }
+       if (!any) {
+               ipath_dbg("port %u, all pkeys already in use, "
+                         "can't set %x\n", pd->port_port, key);
+               ret = -EBUSY;
+               goto bail;
+       }
+       for (any = i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) {
+               if (!dd->ipath_pkeys[i] &&
+                   atomic_inc_return(&dd->ipath_pkeyrefs[i]) == 1) {
+                       u64 pkey;
+
+                       /* for ipathstats, etc. */
+                       ipath_stats.sps_pkeys[i] = lkey;
+                       pd->port_pkeys[pidx] = dd->ipath_pkeys[i] = key;
+                       pkey =
+                               (u64) dd->ipath_pkeys[0] |
+                               ((u64) dd->ipath_pkeys[1] << 16) |
+                               ((u64) dd->ipath_pkeys[2] << 32) |
+                               ((u64) dd->ipath_pkeys[3] << 48);
+                       ipath_cdbg(PROC, "p%u set key %x in #%d, "
+                                  "portidx %d, new pkey reg %llx\n",
+                                  pd->port_port, key, i, pidx,
+                                  (unsigned long long) pkey);
+                       ipath_write_kreg(
+                               dd, dd->ipath_kregs->kr_partitionkey, pkey);
+
+                       ret = 0;
+                       goto bail;
+               }
+       }
+       ipath_dbg("port %u, all pkeys already in use 2nd pass, "
+                 "can't set %x\n", pd->port_port, key);
+       ret = -EBUSY;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_manage_rcvq - manage a port's receive queue
+ * @pd: the port
+ * @start_stop: action to carry out
+ *
+ * start_stop == 0 disables receive on the port, for use in queue
+ * overflow conditions.  start_stop==1 re-enables, to be used to
+ * re-init the software copy of the head register
+ */
+static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
+{
+       struct ipath_devdata *dd = pd->port_dd;
+       u64 tval;
+
+       ipath_cdbg(PROC, "%sabling rcv for unit %u port %u\n",
+                  start_stop ? "en" : "dis", dd->ipath_unit,
+                  pd->port_port);
+       /* atomically clear receive enable port. */
+       if (start_stop) {
+               /*
+                * On enable, force in-memory copy of the tail register to
+                * 0, so that protocol code doesn't have to worry about
+                * whether or not the chip has yet updated the in-memory
+                * copy or not on return from the system call. The chip
+                * always resets it's tail register back to 0 on a
+                * transition from disabled to enabled.  This could cause a
+                * problem if software was broken, and did the enable w/o
+                * the disable, but eventually the in-memory copy will be
+                * updated and correct itself, even in the face of software
+                * bugs.
+                */
+               *pd->port_rcvhdrtail_kvaddr = 0;
+               set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
+                       &dd->ipath_rcvctrl);
+       } else
+               clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
+                         &dd->ipath_rcvctrl);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+                        dd->ipath_rcvctrl);
+       /* now be sure chip saw it before we return */
+       tval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+       if (start_stop) {
+               /*
+                * And try to be sure that tail reg update has happened too.
+                * This should in theory interlock with the RXE changes to
+                * the tail register.  Don't assign it to the tail register
+                * in memory copy, since we could overwrite an update by the
+                * chip if we did.
+                */
+               tval = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
+       }
+       /* always; new head should be equal to new tail; see above */
+       return 0;
+}
+
+static void ipath_clean_part_key(struct ipath_portdata *pd,
+                                struct ipath_devdata *dd)
+{
+       int i, j, pchanged = 0;
+       u64 oldpkey;
+
+       /* for debugging only */
+       oldpkey = (u64) dd->ipath_pkeys[0] |
+               ((u64) dd->ipath_pkeys[1] << 16) |
+               ((u64) dd->ipath_pkeys[2] << 32) |
+               ((u64) dd->ipath_pkeys[3] << 48);
+
+       for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) {
+               if (!pd->port_pkeys[i])
+                       continue;
+               ipath_cdbg(VERBOSE, "look for key[%d] %hx in pkeys\n", i,
+                          pd->port_pkeys[i]);
+               for (j = 0; j < ARRAY_SIZE(dd->ipath_pkeys); j++) {
+                       /* check for match independent of the global bit */
+                       if ((dd->ipath_pkeys[j] & 0x7fff) !=
+                           (pd->port_pkeys[i] & 0x7fff))
+                               continue;
+                       if (atomic_dec_and_test(&dd->ipath_pkeyrefs[j])) {
+                               ipath_cdbg(VERBOSE, "p%u clear key "
+                                          "%x matches #%d\n",
+                                          pd->port_port,
+                                          pd->port_pkeys[i], j);
+                               ipath_stats.sps_pkeys[j] =
+                                       dd->ipath_pkeys[j] = 0;
+                               pchanged++;
+                       }
+                       else ipath_cdbg(
+                               VERBOSE, "p%u key %x matches #%d, "
+                               "but ref still %d\n", pd->port_port,
+                               pd->port_pkeys[i], j,
+                               atomic_read(&dd->ipath_pkeyrefs[j]));
+                       break;
+               }
+               pd->port_pkeys[i] = 0;
+       }
+       if (pchanged) {
+               u64 pkey = (u64) dd->ipath_pkeys[0] |
+                       ((u64) dd->ipath_pkeys[1] << 16) |
+                       ((u64) dd->ipath_pkeys[2] << 32) |
+                       ((u64) dd->ipath_pkeys[3] << 48);
+               ipath_cdbg(VERBOSE, "p%u old pkey reg %llx, "
+                          "new pkey reg %llx\n", pd->port_port,
+                          (unsigned long long) oldpkey,
+                          (unsigned long long) pkey);
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_partitionkey,
+                                pkey);
+       }
+}
+
+/**
+ * ipath_create_user_egr - allocate eager TID buffers
+ * @pd: the port to allocate TID buffers for
+ *
+ * This routine is now quite different for user and kernel, because
+ * the kernel uses skb's, for the accelerated network performance
+ * This is the user port version
+ *
+ * Allocate the eager TID buffers and program them into infinipath
+ * They are no longer completely contiguous, we do multiple allocation
+ * calls.
+ */
+static int ipath_create_user_egr(struct ipath_portdata *pd)
+{
+       struct ipath_devdata *dd = pd->port_dd;
+       unsigned e, egrcnt, alloced, egrperchunk, chunk, egrsize, egroff;
+       size_t size;
+       int ret;
+
+       egrcnt = dd->ipath_rcvegrcnt;
+       /* TID number offset for this port */
+       egroff = pd->port_port * egrcnt;
+       egrsize = dd->ipath_rcvegrbufsize;
+       ipath_cdbg(VERBOSE, "Allocating %d egr buffers, at egrtid "
+                  "offset %x, egrsize %u\n", egrcnt, egroff, egrsize);
+
+       /*
+        * to avoid wasting a lot of memory, we allocate 32KB chunks of
+        * physically contiguous memory, advance through it until used up
+        * and then allocate more.  Of course, we need memory to store those
+        * extra pointers, now.  Started out with 256KB, but under heavy
+        * memory pressure (creating large files and then copying them over
+        * NFS while doing lots of MPI jobs), we hit some allocation
+        * failures, even though we can sleep...  (2.6.10) Still get
+        * failures at 64K.  32K is the lowest we can go without waiting
+        * more memory again.  It seems likely that the coalescing in
+        * free_pages, etc. still has issues (as it has had previously
+        * during 2.6.x development).
+        */
+       size = 0x8000;
+       alloced = ALIGN(egrsize * egrcnt, size);
+       egrperchunk = size / egrsize;
+       chunk = (egrcnt + egrperchunk - 1) / egrperchunk;
+       pd->port_rcvegrbuf_chunks = chunk;
+       pd->port_rcvegrbufs_perchunk = egrperchunk;
+       pd->port_rcvegrbuf_size = size;
+       pd->port_rcvegrbuf = vmalloc(chunk * sizeof(pd->port_rcvegrbuf[0]));
+       if (!pd->port_rcvegrbuf) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+       pd->port_rcvegrbuf_phys =
+               vmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0]));
+       if (!pd->port_rcvegrbuf_phys) {
+               ret = -ENOMEM;
+               goto bail_rcvegrbuf;
+       }
+       for (e = 0; e < pd->port_rcvegrbuf_chunks; e++) {
+               /*
+                * GFP_USER, but without GFP_FS, so buffer cache can be
+                * coalesced (we hope); otherwise, even at order 4,
+                * heavy filesystem activity makes these fail
+                */
+               gfp_t gfp_flags = __GFP_WAIT | __GFP_IO | __GFP_COMP;
+
+               pd->port_rcvegrbuf[e] = dma_alloc_coherent(
+                       &dd->pcidev->dev, size, &pd->port_rcvegrbuf_phys[e],
+                       gfp_flags);
+
+               if (!pd->port_rcvegrbuf[e]) {
+                       ret = -ENOMEM;
+                       goto bail_rcvegrbuf_phys;
+               }
+       }
+
+       pd->port_rcvegr_phys = pd->port_rcvegrbuf_phys[0];
+
+       for (e = chunk = 0; chunk < pd->port_rcvegrbuf_chunks; chunk++) {
+               dma_addr_t pa = pd->port_rcvegrbuf_phys[chunk];
+               unsigned i;
+
+               for (i = 0; e < egrcnt && i < egrperchunk; e++, i++) {
+                       dd->ipath_f_put_tid(dd, e + egroff +
+                                           (u64 __iomem *)
+                                           ((char __iomem *)
+                                            dd->ipath_kregbase +
+                                            dd->ipath_rcvegrbase), 0, pa);
+                       pa += egrsize;
+               }
+               cond_resched(); /* don't hog the cpu */
+       }
+
+       ret = 0;
+       goto bail;
+
+bail_rcvegrbuf_phys:
+       for (e = 0; e < pd->port_rcvegrbuf_chunks &&
+                    pd->port_rcvegrbuf[e]; e++)
+               dma_free_coherent(&dd->pcidev->dev, size,
+                                 pd->port_rcvegrbuf[e],
+                                 pd->port_rcvegrbuf_phys[e]);
+
+       vfree(pd->port_rcvegrbuf_phys);
+       pd->port_rcvegrbuf_phys = NULL;
+bail_rcvegrbuf:
+       vfree(pd->port_rcvegrbuf);
+       pd->port_rcvegrbuf = NULL;
+bail:
+       return ret;
+}
+
+static int ipath_do_user_init(struct ipath_portdata *pd,
+                             const struct ipath_user_info *uinfo)
+{
+       int ret = 0;
+       struct ipath_devdata *dd = pd->port_dd;
+       u64 physaddr, uaddr, off, atmp;
+       struct page *pagep;
+       u32 head32;
+       u64 head;
+
+       /* for now, if major version is different, bail */
+       if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) {
+               dev_info(&dd->pcidev->dev,
+                        "User major version %d not same as driver "
+                        "major %d\n", uinfo->spu_userversion >> 16,
+                        IPATH_USER_SWMAJOR);
+               ret = -ENODEV;
+               goto done;
+       }
+
+       if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR)
+               ipath_dbg("User minor version %d not same as driver "
+                         "minor %d\n", uinfo->spu_userversion & 0xffff,
+                         IPATH_USER_SWMINOR);
+
+       if (uinfo->spu_rcvhdrsize) {
+               ret = ipath_setrcvhdrsize(dd, uinfo->spu_rcvhdrsize);
+               if (ret)
+                       goto done;
+       }
+
+       /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */
+
+       /* set up for the rcvhdr Q tail register writeback to user memory */
+       if (!uinfo->spu_rcvhdraddr ||
+           !access_ok(VERIFY_WRITE, (u64 __user *) (unsigned long)
+                      uinfo->spu_rcvhdraddr, sizeof(u64))) {
+               ipath_dbg("Port %d rcvhdrtail addr %llx not valid\n",
+                         pd->port_port,
+                         (unsigned long long) uinfo->spu_rcvhdraddr);
+               ret = -EINVAL;
+               goto done;
+       }
+
+       off = offset_in_page(uinfo->spu_rcvhdraddr);
+       uaddr = PAGE_MASK & (unsigned long) uinfo->spu_rcvhdraddr;
+       ret = ipath_get_user_pages_nocopy(uaddr, &pagep);
+       if (ret) {
+               dev_info(&dd->pcidev->dev, "Failed to lookup and lock "
+                        "address %llx for rcvhdrtail: errno %d\n",
+                        (unsigned long long) uinfo->spu_rcvhdraddr, -ret);
+               goto done;
+       }
+       ipath_stats.sps_pagelocks++;
+       pd->port_rcvhdrtail_uaddr = uaddr;
+       pd->port_rcvhdrtail_pagep = pagep;
+       pd->port_rcvhdrtail_kvaddr =
+               page_address(pagep);
+       pd->port_rcvhdrtail_kvaddr += off;
+       physaddr = page_to_phys(pagep) + off;
+       ipath_cdbg(VERBOSE, "port %d user addr %llx hdrtailaddr, %llx "
+                  "physical (off=%llx)\n",
+                  pd->port_port,
+                  (unsigned long long) uinfo->spu_rcvhdraddr,
+                  (unsigned long long) physaddr, (unsigned long long) off);
+       ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdrtailaddr,
+                             pd->port_port, physaddr);
+       atmp = ipath_read_kreg64_port(dd,
+                                     dd->ipath_kregs->kr_rcvhdrtailaddr,
+                                     pd->port_port);
+       if (physaddr != atmp) {
+               ipath_dev_err(dd,
+                             "Catastrophic software error, "
+                             "RcvHdrTailAddr%u written as %llx, "
+                             "read back as %llx\n", pd->port_port,
+                             (unsigned long long) physaddr,
+                             (unsigned long long) atmp);
+               ret = -EINVAL;
+               goto done;
+       }
+
+       /* for right now, kernel piobufs are at end, so port 1 is at 0 */
+       pd->port_piobufs = dd->ipath_piobufbase +
+               dd->ipath_pbufsport * (pd->port_port -
+                                      1) * dd->ipath_palign;
+       ipath_cdbg(VERBOSE, "Set base of piobufs for port %u to 0x%x\n",
+                  pd->port_port, pd->port_piobufs);
+
+       /*
+        * Now allocate the rcvhdr Q and eager TIDs; skip the TID
+        * array for time being.  If pd->port_port > chip-supported,
+        * we need to do extra stuff here to handle by handling overflow
+        * through port 0, someday
+        */
+       ret = ipath_create_rcvhdrq(dd, pd);
+       if (!ret)
+               ret = ipath_create_user_egr(pd);
+       if (ret)
+               goto done;
+       /* enable receives now */
+       /* atomically set enable bit for this port */
+       set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
+               &dd->ipath_rcvctrl);
+
+       /*
+        * set the head registers for this port to the current values
+        * of the tail pointers, since we don't know if they were
+        * updated on last use of the port.
+        */
+       head32 = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
+       head = (u64) head32;
+       ipath_write_ureg(dd, ur_rcvhdrhead, head, pd->port_port);
+       head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port);
+       ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port);
+       dd->ipath_lastegrheads[pd->port_port] = -1;
+       dd->ipath_lastrcvhdrqtails[pd->port_port] = -1;
+       ipath_cdbg(VERBOSE, "Wrote port%d head %llx, egrhead %x from "
+                  "tail regs\n", pd->port_port,
+                  (unsigned long long) head, head32);
+       pd->port_tidcursor = 0; /* start at beginning after open */
+       /*
+        * now enable the port; the tail registers will be written to memory
+        * by the chip as soon as it sees the write to
+        * dd->ipath_kregs->kr_rcvctrl.  The update only happens on
+        * transition from 0 to 1, so clear it first, then set it as part of
+        * enabling the port.  This will (very briefly) affect any other
+        * open ports, but it shouldn't be long enough to be an issue.
+        */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+                        dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+                        dd->ipath_rcvctrl);
+
+done:
+       return ret;
+}
+
+static int mmap_ureg(struct vm_area_struct *vma, struct ipath_devdata *dd,
+                    u64 ureg)
+{
+       unsigned long phys;
+       int ret;
+
+       /* it's the real hardware, so io_remap works */
+
+       if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) {
+               dev_info(&dd->pcidev->dev, "FAIL mmap userreg: reqlen "
+                        "%lx > PAGE\n", vma->vm_end - vma->vm_start);
+               ret = -EFAULT;
+       } else {
+               phys = dd->ipath_physaddr + ureg;
+               vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+               vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND;
+               ret = io_remap_pfn_range(vma, vma->vm_start,
+                                        phys >> PAGE_SHIFT,
+                                        vma->vm_end - vma->vm_start,
+                                        vma->vm_page_prot);
+       }
+       return ret;
+}
+
+static int mmap_piobufs(struct vm_area_struct *vma,
+                       struct ipath_devdata *dd,
+                       struct ipath_portdata *pd)
+{
+       unsigned long phys;
+       int ret;
+
+       /*
+        * When we map the PIO buffers, we want to map them as writeonly, no
+        * read possible.
+        */
+
+       if ((vma->vm_end - vma->vm_start) >
+           (dd->ipath_pbufsport * dd->ipath_palign)) {
+               dev_info(&dd->pcidev->dev, "FAIL mmap piobufs: "
+                        "reqlen %lx > PAGE\n",
+                        vma->vm_end - vma->vm_start);
+               ret = -EFAULT;
+               goto bail;
+       }
+
+       phys = dd->ipath_physaddr + pd->port_piobufs;
+       /*
+        * Do *NOT* mark this as non-cached (PWT bit), or we don't get the
+        * write combining behavior we want on the PIO buffers!
+        * vma->vm_page_prot =
+        *        pgprot_noncached(vma->vm_page_prot);
+        */
+
+       if (vma->vm_flags & VM_READ) {
+               dev_info(&dd->pcidev->dev,
+                        "Can't map piobufs as readable (flags=%lx)\n",
+                        vma->vm_flags);
+               ret = -EPERM;
+               goto bail;
+       }
+
+       /* don't allow them to later change to readable with mprotect */
+
+       vma->vm_flags &= ~VM_MAYWRITE;
+       vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND;
+
+       ret = io_remap_pfn_range(vma, vma->vm_start, phys >> PAGE_SHIFT,
+                                vma->vm_end - vma->vm_start,
+                                vma->vm_page_prot);
+bail:
+       return ret;
+}
+
+static int mmap_rcvegrbufs(struct vm_area_struct *vma,
+                          struct ipath_portdata *pd)
+{
+       struct ipath_devdata *dd = pd->port_dd;
+       unsigned long start, size;
+       size_t total_size, i;
+       dma_addr_t *phys;
+       int ret;
+
+       if (!pd->port_rcvegrbuf) {
+               ret = -EFAULT;
+               goto bail;
+       }
+
+       size = pd->port_rcvegrbuf_size;
+       total_size = pd->port_rcvegrbuf_chunks * size;
+       if ((vma->vm_end - vma->vm_start) > total_size) {
+               dev_info(&dd->pcidev->dev, "FAIL on egr bufs: "
+                        "reqlen %lx > actual %lx\n",
+                        vma->vm_end - vma->vm_start,
+                        (unsigned long) total_size);
+               ret = -EFAULT;
+               goto bail;
+       }
+
+       if (vma->vm_flags & VM_WRITE) {
+               dev_info(&dd->pcidev->dev, "Can't map eager buffers as "
+                        "writable (flags=%lx)\n", vma->vm_flags);
+               ret = -EPERM;
+               goto bail;
+       }
+
+       start = vma->vm_start;
+       phys = pd->port_rcvegrbuf_phys;
+
+       /* don't allow them to later change to writeable with mprotect */
+       vma->vm_flags &= ~VM_MAYWRITE;
+
+       for (i = 0; i < pd->port_rcvegrbuf_chunks; i++, start += size) {
+               ret = remap_pfn_range(vma, start, phys[i] >> PAGE_SHIFT,
+                                     size, vma->vm_page_prot);
+               if (ret < 0)
+                       goto bail;
+       }
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+static int mmap_rcvhdrq(struct vm_area_struct *vma,
+                       struct ipath_portdata *pd)
+{
+       struct ipath_devdata *dd = pd->port_dd;
+       size_t total_size;
+       int ret;
+
+       /*
+        * kmalloc'ed memory, physically contiguous; this is from
+        * spi_rcvhdr_base; we allow user to map read-write so they can
+        * write hdrq entries to allow protocol code to directly poll
+        * whether a hdrq entry has been written.
+        */
+       total_size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize *
+                          sizeof(u32), PAGE_SIZE);
+       if ((vma->vm_end - vma->vm_start) > total_size) {
+               dev_info(&dd->pcidev->dev,
+                        "FAIL on rcvhdrq: reqlen %lx > actual %lx\n",
+                        vma->vm_end - vma->vm_start,
+                        (unsigned long) total_size);
+               ret = -EFAULT;
+               goto bail;
+       }
+
+       ret = remap_pfn_range(vma, vma->vm_start,
+                             pd->port_rcvhdrq_phys >> PAGE_SHIFT,
+                             vma->vm_end - vma->vm_start,
+                             vma->vm_page_prot);
+bail:
+       return ret;
+}
+
+static int mmap_pioavailregs(struct vm_area_struct *vma,
+                            struct ipath_portdata *pd)
+{
+       struct ipath_devdata *dd = pd->port_dd;
+       int ret;
+
+       /*
+        * when we map the PIO bufferavail registers, we want to map them as
+        * readonly, no write possible.
+        *
+        * kmalloc'ed memory, physically contiguous, one page only, readonly
+        */
+
+       if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) {
+               dev_info(&dd->pcidev->dev, "FAIL on pioavailregs_dma: "
+                        "reqlen %lx > actual %lx\n",
+                        vma->vm_end - vma->vm_start,
+                        (unsigned long) PAGE_SIZE);
+               ret = -EFAULT;
+               goto bail;
+       }
+
+       if (vma->vm_flags & VM_WRITE) {
+               dev_info(&dd->pcidev->dev,
+                        "Can't map pioavailregs as writable (flags=%lx)\n",
+                        vma->vm_flags);
+               ret = -EPERM;
+               goto bail;
+       }
+
+       /* don't allow them to later change with mprotect */
+       vma->vm_flags &= ~VM_MAYWRITE;
+
+       ret = remap_pfn_range(vma, vma->vm_start,
+                             dd->ipath_pioavailregs_phys >> PAGE_SHIFT,
+                             PAGE_SIZE, vma->vm_page_prot);
+bail:
+       return ret;
+}
+
+/**
+ * ipath_mmap - mmap various structures into user space
+ * @fp: the file pointer
+ * @vma: the VM area
+ *
+ * We use this to have a shared buffer between the kernel and the user code
+ * for the rcvhdr queue, egr buffers, and the per-port user regs and pio
+ * buffers in the chip.  We have the open and close entries so we can bump
+ * the ref count and keep the driver from being unloaded while still mapped.
+ */
+static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
+{
+       struct ipath_portdata *pd;
+       struct ipath_devdata *dd;
+       u64 pgaddr, ureg;
+       int ret;
+
+       pd = port_fp(fp);
+       dd = pd->port_dd;
+       /*
+        * This is the ipath_do_user_init() code, mapping the shared buffers
+        * into the user process. The address referred to by vm_pgoff is the
+        * virtual, not physical, address; we only do one mmap for each
+        * space mapped.
+        */
+       pgaddr = vma->vm_pgoff << PAGE_SHIFT;
+
+       /*
+        * note that ureg does *NOT* have the kregvirt as part of it, to be
+        * sure that for 32 bit programs, we don't end up trying to map a >
+        * 44 address.  Has to match ipath_get_base_info() code that sets
+        * __spi_uregbase
+        */
+
+       ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
+
+       ipath_cdbg(MM, "ushare: pgaddr %llx vm_start=%lx, vmlen %lx\n",
+                  (unsigned long long) pgaddr, vma->vm_start,
+                  vma->vm_end - vma->vm_start);
+
+       if (pgaddr == ureg)
+               ret = mmap_ureg(vma, dd, ureg);
+       else if (pgaddr == pd->port_piobufs)
+               ret = mmap_piobufs(vma, dd, pd);
+       else if (pgaddr == (u64) pd->port_rcvegr_phys)
+               ret = mmap_rcvegrbufs(vma, pd);
+       else if (pgaddr == (u64) pd->port_rcvhdrq_phys)
+               ret = mmap_rcvhdrq(vma, pd);
+       else if (pgaddr == dd->ipath_pioavailregs_phys)
+               ret = mmap_pioavailregs(vma, pd);
+       else
+               ret = -EINVAL;
+
+       vma->vm_private_data = NULL;
+
+       if (ret < 0)
+               dev_info(&dd->pcidev->dev,
+                        "Failure %d on addr %lx, off %lx\n",
+                        -ret, vma->vm_start, vma->vm_pgoff);
+
+       return ret;
+}
+
+static unsigned int ipath_poll(struct file *fp,
+                              struct poll_table_struct *pt)
+{
+       struct ipath_portdata *pd;
+       u32 head, tail;
+       int bit;
+       struct ipath_devdata *dd;
+
+       pd = port_fp(fp);
+       dd = pd->port_dd;
+
+       bit = pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT;
+       set_bit(bit, &dd->ipath_rcvctrl);
+
+       /*
+        * Before blocking, make sure that head is still == tail,
+        * reading from the chip, so we can be sure the interrupt
+        * enable has made it to the chip.  If not equal, disable
+        * interrupt again and return immediately.  This avoids races,
+        * and the overhead of the chip read doesn't matter much at
+        * this point, since we are waiting for something anyway.
+        */
+
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+                        dd->ipath_rcvctrl);
+
+       head = ipath_read_ureg32(dd, ur_rcvhdrhead, pd->port_port);
+       tail = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
+
+       if (tail == head) {
+               set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);
+               poll_wait(fp, &pd->port_wait, pt);
+
+               if (test_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag)) {
+                       /* timed out, no packets received */
+                       clear_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);
+                       pd->port_rcvwait_to++;
+               }
+       }
+       else {
+               /* it's already happened; don't do wait_event overhead */
+               pd->port_rcvnowait++;
+       }
+
+       clear_bit(bit, &dd->ipath_rcvctrl);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+                        dd->ipath_rcvctrl);
+
+       return 0;
+}
+
+static int try_alloc_port(struct ipath_devdata *dd, int port,
+                         struct file *fp)
+{
+       int ret;
+
+       if (!dd->ipath_pd[port]) {
+               void *p, *ptmp;
+
+               p = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL);
+
+               /*
+                * Allocate memory for use in ipath_tid_update() just once
+                * at open, not per call.  Reduces cost of expected send
+                * setup.
+                */
+               ptmp = kmalloc(dd->ipath_rcvtidcnt * sizeof(u16) +
+                              dd->ipath_rcvtidcnt * sizeof(struct page **),
+                              GFP_KERNEL);
+               if (!p || !ptmp) {
+                       ipath_dev_err(dd, "Unable to allocate portdata "
+                                     "memory, failing open\n");
+                       ret = -ENOMEM;
+                       kfree(p);
+                       kfree(ptmp);
+                       goto bail;
+               }
+               dd->ipath_pd[port] = p;
+               dd->ipath_pd[port]->port_port = port;
+               dd->ipath_pd[port]->port_dd = dd;
+               dd->ipath_pd[port]->port_tid_pg_list = ptmp;
+               init_waitqueue_head(&dd->ipath_pd[port]->port_wait);
+       }
+       if (!dd->ipath_pd[port]->port_cnt) {
+               dd->ipath_pd[port]->port_cnt = 1;
+               fp->private_data = (void *) dd->ipath_pd[port];
+               ipath_cdbg(PROC, "%s[%u] opened unit:port %u:%u\n",
+                          current->comm, current->pid, dd->ipath_unit,
+                          port);
+               dd->ipath_pd[port]->port_pid = current->pid;
+               strncpy(dd->ipath_pd[port]->port_comm, current->comm,
+                       sizeof(dd->ipath_pd[port]->port_comm));
+               ipath_stats.sps_ports++;
+               ret = 0;
+               goto bail;
+       }
+       ret = -EBUSY;
+
+bail:
+       return ret;
+}
+
+static inline int usable(struct ipath_devdata *dd)
+{
+       return dd &&
+               (dd->ipath_flags & IPATH_PRESENT) &&
+               dd->ipath_kregbase &&
+               dd->ipath_lid &&
+               !(dd->ipath_flags & (IPATH_LINKDOWN | IPATH_DISABLED
+                                    | IPATH_LINKUNK));
+}
+
+static int find_free_port(int unit, struct file *fp)
+{
+       struct ipath_devdata *dd = ipath_lookup(unit);
+       int ret, i;
+
+       if (!dd) {
+               ret = -ENODEV;
+               goto bail;
+       }
+
+       if (!usable(dd)) {
+               ret = -ENETDOWN;
+               goto bail;
+       }
+
+       for (i = 0; i < dd->ipath_cfgports; i++) {
+               ret = try_alloc_port(dd, i, fp);
+               if (ret != -EBUSY)
+                       goto bail;
+       }
+       ret = -EBUSY;
+
+bail:
+       return ret;
+}
+
+static int find_best_unit(struct file *fp)
+{
+       int ret = 0, i, prefunit = -1, devmax;
+       int maxofallports, npresent, nup;
+       int ndev;
+
+       (void) ipath_count_units(&npresent, &nup, &maxofallports);
+
+       /*
+        * This code is present to allow a knowledgeable person to
+        * specify the layout of processes to processors before opening
+        * this driver, and then we'll assign the process to the "closest"
+        * HT-400 to that processor (we assume reasonable connectivity,
+        * for now).  This code assumes that if affinity has been set
+        * before this point, that at most one cpu is set; for now this
+        * is reasonable.  I check for both cpus_empty() and cpus_full(),
+        * in case some kernel variant sets none of the bits when no
+        * affinity is set.  2.6.11 and 12 kernels have all present
+        * cpus set.  Some day we'll have to fix it up further to handle
+        * a cpu subset.  This algorithm fails for two HT-400's connected
+        * in tunnel fashion.  Eventually this needs real topology
+        * information.  There may be some issues with dual core numbering
+        * as well.  This needs more work prior to release.
+        */
+       if (!cpus_empty(current->cpus_allowed) &&
+           !cpus_full(current->cpus_allowed)) {
+               int ncpus = num_online_cpus(), curcpu = -1;
+               for (i = 0; i < ncpus; i++)
+                       if (cpu_isset(i, current->cpus_allowed)) {
+                               ipath_cdbg(PROC, "%s[%u] affinity set for "
+                                          "cpu %d\n", current->comm,
+                                          current->pid, i);
+                               curcpu = i;
+                       }
+               if (curcpu != -1) {
+                       if (npresent) {
+                               prefunit = curcpu / (ncpus / npresent);
+                               ipath_dbg("%s[%u] %d chips, %d cpus, "
+                                         "%d cpus/chip, select unit %d\n",
+                                         current->comm, current->pid,
+                                         npresent, ncpus, ncpus / npresent,
+                                         prefunit);
+                       }
+               }
+       }
+
+       /*
+        * user ports start at 1, kernel port is 0
+        * For now, we do round-robin access across all chips
+        */
+
+       if (prefunit != -1)
+               devmax = prefunit + 1;
+       else
+               devmax = ipath_count_units(NULL, NULL, NULL);
+recheck:
+       for (i = 1; i < maxofallports; i++) {
+               for (ndev = prefunit != -1 ? prefunit : 0; ndev < devmax;
+                    ndev++) {
+                       struct ipath_devdata *dd = ipath_lookup(ndev);
+
+                       if (!usable(dd))
+                               continue; /* can't use this unit */
+                       if (i >= dd->ipath_cfgports)
+                               /*
+                                * Maxed out on users of this unit. Try
+                                * next.
+                                */
+                               continue;
+                       ret = try_alloc_port(dd, i, fp);
+                       if (!ret)
+                               goto done;
+               }
+       }
+
+       if (npresent) {
+               if (nup == 0) {
+                       ret = -ENETDOWN;
+                       ipath_dbg("No ports available (none initialized "
+                                 "and ready)\n");
+               } else {
+                       if (prefunit > 0) {
+                               /* if started above 0, retry from 0 */
+                               ipath_cdbg(PROC,
+                                          "%s[%u] no ports on prefunit "
+                                          "%d, clear and re-check\n",
+                                          current->comm, current->pid,
+                                          prefunit);
+                               devmax = ipath_count_units(NULL, NULL,
+                                                          NULL);
+                               prefunit = -1;
+                               goto recheck;
+                       }
+                       ret = -EBUSY;
+                       ipath_dbg("No ports available\n");
+               }
+       } else {
+               ret = -ENXIO;
+               ipath_dbg("No boards found\n");
+       }
+
+done:
+       return ret;
+}
+
+static int ipath_open(struct inode *in, struct file *fp)
+{
+       int ret, minor;
+
+       mutex_lock(&ipath_mutex);
+
+       minor = iminor(in);
+       ipath_cdbg(VERBOSE, "open on dev %lx (minor %d)\n",
+                  (long)in->i_rdev, minor);
+
+       if (minor)
+               ret = find_free_port(minor - 1, fp);
+       else
+               ret = find_best_unit(fp);
+
+       mutex_unlock(&ipath_mutex);
+       return ret;
+}
+
+/**
+ * unlock_exptid - unlock any expected TID entries port still had in use
+ * @pd: port
+ *
+ * We don't actually update the chip here, because we do a bulk update
+ * below, using ipath_f_clear_tids.
+ */
+static void unlock_expected_tids(struct ipath_portdata *pd)
+{
+       struct ipath_devdata *dd = pd->port_dd;
+       int port_tidbase = pd->port_port * dd->ipath_rcvtidcnt;
+       int i, cnt = 0, maxtid = port_tidbase + dd->ipath_rcvtidcnt;
+
+       ipath_cdbg(VERBOSE, "Port %u unlocking any locked expTID pages\n",
+                  pd->port_port);
+       for (i = port_tidbase; i < maxtid; i++) {
+               if (!dd->ipath_pageshadow[i])
+                       continue;
+
+               ipath_release_user_pages_on_close(&dd->ipath_pageshadow[i],
+                                                 1);
+               dd->ipath_pageshadow[i] = NULL;
+               cnt++;
+               ipath_stats.sps_pageunlocks++;
+       }
+       if (cnt)
+               ipath_cdbg(VERBOSE, "Port %u locked %u expTID entries\n",
+                          pd->port_port, cnt);
+
+       if (ipath_stats.sps_pagelocks || ipath_stats.sps_pageunlocks)
+               ipath_cdbg(VERBOSE, "%llu pages locked, %llu unlocked\n",
+                          (unsigned long long) ipath_stats.sps_pagelocks,
+                          (unsigned long long)
+                          ipath_stats.sps_pageunlocks);
+}
+
+static int ipath_close(struct inode *in, struct file *fp)
+{
+       int ret = 0;
+       struct ipath_portdata *pd;
+       struct ipath_devdata *dd;
+       unsigned port;
+
+       ipath_cdbg(VERBOSE, "close on dev %lx, private data %p\n",
+                  (long)in->i_rdev, fp->private_data);
+
+       mutex_lock(&ipath_mutex);
+
+       pd = port_fp(fp);
+       port = pd->port_port;
+       fp->private_data = NULL;
+       dd = pd->port_dd;
+
+       if (pd->port_hdrqfull) {
+               ipath_cdbg(PROC, "%s[%u] had %u rcvhdrqfull errors "
+                          "during run\n", pd->port_comm, pd->port_pid,
+                          pd->port_hdrqfull);
+               pd->port_hdrqfull = 0;
+       }
+
+       if (pd->port_rcvwait_to || pd->port_piowait_to
+           || pd->port_rcvnowait || pd->port_pionowait) {
+               ipath_cdbg(VERBOSE, "port%u, %u rcv, %u pio wait timeo; "
+                          "%u rcv %u, pio already\n",
+                          pd->port_port, pd->port_rcvwait_to,
+                          pd->port_piowait_to, pd->port_rcvnowait,
+                          pd->port_pionowait);
+               pd->port_rcvwait_to = pd->port_piowait_to =
+                       pd->port_rcvnowait = pd->port_pionowait = 0;
+       }
+       if (pd->port_flag) {
+               ipath_dbg("port %u port_flag still set to 0x%lx\n",
+                         pd->port_port, pd->port_flag);
+               pd->port_flag = 0;
+       }
+
+       if (dd->ipath_kregbase) {
+               if (pd->port_rcvhdrtail_uaddr) {
+                       pd->port_rcvhdrtail_uaddr = 0;
+                       pd->port_rcvhdrtail_kvaddr = NULL;
+                       ipath_release_user_pages_on_close(
+                               &pd->port_rcvhdrtail_pagep, 1);
+                       pd->port_rcvhdrtail_pagep = NULL;
+                       ipath_stats.sps_pageunlocks++;
+               }
+               ipath_write_kreg_port(
+                       dd, dd->ipath_kregs->kr_rcvhdrtailaddr,
+                       port, 0ULL);
+               ipath_write_kreg_port(
+                       dd, dd->ipath_kregs->kr_rcvhdraddr,
+                       pd->port_port, 0);
+
+               /* clean up the pkeys for this port user */
+               ipath_clean_part_key(pd, dd);
+
+               if (port < dd->ipath_cfgports) {
+                       int i = dd->ipath_pbufsport * (port - 1);
+                       ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport);
+
+                       /* atomically clear receive enable port. */
+                       clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port,
+                                 &dd->ipath_rcvctrl);
+                       ipath_write_kreg(
+                               dd,
+                               dd->ipath_kregs->kr_rcvctrl,
+                               dd->ipath_rcvctrl);
+
+                       if (dd->ipath_pageshadow)
+                               unlock_expected_tids(pd);
+                       ipath_stats.sps_ports--;
+                       ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n",
+                                  pd->port_comm, pd->port_pid,
+                                  dd->ipath_unit, port);
+               }
+       }
+
+       pd->port_cnt = 0;
+       pd->port_pid = 0;
+
+       dd->ipath_f_clear_tids(dd, pd->port_port);
+
+       ipath_free_pddata(dd, pd->port_port, 0);
+
+       mutex_unlock(&ipath_mutex);
+
+       return ret;
+}
+
+static int ipath_port_info(struct ipath_portdata *pd,
+                          struct ipath_port_info __user *uinfo)
+{
+       struct ipath_port_info info;
+       int nup;
+       int ret;
+
+       (void) ipath_count_units(NULL, &nup, NULL);
+       info.num_active = nup;
+       info.unit = pd->port_dd->ipath_unit;
+       info.port = pd->port_port;
+
+       if (copy_to_user(uinfo, &info, sizeof(info))) {
+               ret = -EFAULT;
+               goto bail;
+       }
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+static ssize_t ipath_write(struct file *fp, const char __user *data,
+                          size_t count, loff_t *off)
+{
+       const struct ipath_cmd __user *ucmd;
+       struct ipath_portdata *pd;
+       const void __user *src;
+       size_t consumed, copy;
+       struct ipath_cmd cmd;
+       ssize_t ret = 0;
+       void *dest;
+
+       if (count < sizeof(cmd.type)) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       ucmd = (const struct ipath_cmd __user *) data;
+
+       if (copy_from_user(&cmd.type, &ucmd->type, sizeof(cmd.type))) {
+               ret = -EFAULT;
+               goto bail;
+       }
+
+       consumed = sizeof(cmd.type);
+
+       switch (cmd.type) {
+       case IPATH_CMD_USER_INIT:
+               copy = sizeof(cmd.cmd.user_info);
+               dest = &cmd.cmd.user_info;
+               src = &ucmd->cmd.user_info;
+               break;
+       case IPATH_CMD_RECV_CTRL:
+               copy = sizeof(cmd.cmd.recv_ctrl);
+               dest = &cmd.cmd.recv_ctrl;
+               src = &ucmd->cmd.recv_ctrl;
+               break;
+       case IPATH_CMD_PORT_INFO:
+               copy = sizeof(cmd.cmd.port_info);
+               dest = &cmd.cmd.port_info;
+               src = &ucmd->cmd.port_info;
+               break;
+       case IPATH_CMD_TID_UPDATE:
+       case IPATH_CMD_TID_FREE:
+               copy = sizeof(cmd.cmd.tid_info);
+               dest = &cmd.cmd.tid_info;
+               src = &ucmd->cmd.tid_info;
+               break;
+       case IPATH_CMD_SET_PART_KEY:
+               copy = sizeof(cmd.cmd.part_key);
+               dest = &cmd.cmd.part_key;
+               src = &ucmd->cmd.part_key;
+               break;
+       default:
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       if ((count - consumed) < copy) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       if (copy_from_user(dest, src, copy)) {
+               ret = -EFAULT;
+               goto bail;
+       }
+
+       consumed += copy;
+       pd = port_fp(fp);
+
+       switch (cmd.type) {
+       case IPATH_CMD_USER_INIT:
+               ret = ipath_do_user_init(pd, &cmd.cmd.user_info);
+               if (ret < 0)
+                       goto bail;
+               ret = ipath_get_base_info(
+                       pd, (void __user *) (unsigned long)
+                       cmd.cmd.user_info.spu_base_info,
+                       cmd.cmd.user_info.spu_base_info_size);
+               break;
+       case IPATH_CMD_RECV_CTRL:
+               ret = ipath_manage_rcvq(pd, cmd.cmd.recv_ctrl);
+               break;
+       case IPATH_CMD_PORT_INFO:
+               ret = ipath_port_info(pd,
+                                     (struct ipath_port_info __user *)
+                                     (unsigned long) cmd.cmd.port_info);
+               break;
+       case IPATH_CMD_TID_UPDATE:
+               ret = ipath_tid_update(pd, &cmd.cmd.tid_info);
+               break;
+       case IPATH_CMD_TID_FREE:
+               ret = ipath_tid_free(pd, &cmd.cmd.tid_info);
+               break;
+       case IPATH_CMD_SET_PART_KEY:
+               ret = ipath_set_part_key(pd, cmd.cmd.part_key);
+               break;
+       }
+
+       if (ret >= 0)
+               ret = consumed;
+
+bail:
+       return ret;
+}
+
+static struct class *ipath_class;
+
+static int init_cdev(int minor, char *name, struct file_operations *fops,
+                    struct cdev **cdevp, struct class_device **class_devp)
+{
+       const dev_t dev = MKDEV(IPATH_MAJOR, minor);
+       struct cdev *cdev = NULL;
+       struct class_device *class_dev = NULL;
+       int ret;
+
+       cdev = cdev_alloc();
+       if (!cdev) {
+               printk(KERN_ERR IPATH_DRV_NAME
+                      ": Could not allocate cdev for minor %d, %s\n",
+                      minor, name);
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       cdev->owner = THIS_MODULE;
+       cdev->ops = fops;
+       kobject_set_name(&cdev->kobj, name);
+
+       ret = cdev_add(cdev, dev, 1);
+       if (ret < 0) {
+               printk(KERN_ERR IPATH_DRV_NAME
+                      ": Could not add cdev for minor %d, %s (err %d)\n",
+                      minor, name, -ret);
+               goto err_cdev;
+       }
+
+       class_dev = class_device_create(ipath_class, NULL, dev, NULL, name);
+
+       if (IS_ERR(class_dev)) {
+               ret = PTR_ERR(class_dev);
+               printk(KERN_ERR IPATH_DRV_NAME ": Could not create "
+                      "class_dev for minor %d, %s (err %d)\n",
+                      minor, name, -ret);
+               goto err_cdev;
+       }
+
+       goto done;
+
+err_cdev:
+       cdev_del(cdev);
+       cdev = NULL;
+
+done:
+       if (ret >= 0) {
+               *cdevp = cdev;
+               *class_devp = class_dev;
+       } else {
+               *cdevp = NULL;
+               *class_devp = NULL;
+       }
+
+       return ret;
+}
+
+int ipath_cdev_init(int minor, char *name, struct file_operations *fops,
+                   struct cdev **cdevp, struct class_device **class_devp)
+{
+       return init_cdev(minor, name, fops, cdevp, class_devp);
+}
+
+static void cleanup_cdev(struct cdev **cdevp,
+                        struct class_device **class_devp)
+{
+       struct class_device *class_dev = *class_devp;
+
+       if (class_dev) {
+               class_device_unregister(class_dev);
+               *class_devp = NULL;
+       }
+
+       if (*cdevp) {
+               cdev_del(*cdevp);
+               *cdevp = NULL;
+       }
+}
+
+void ipath_cdev_cleanup(struct cdev **cdevp,
+                       struct class_device **class_devp)
+{
+       cleanup_cdev(cdevp, class_devp);
+}
+
+static struct cdev *wildcard_cdev;
+static struct class_device *wildcard_class_dev;
+
+static const dev_t dev = MKDEV(IPATH_MAJOR, 0);
+
+static int user_init(void)
+{
+       int ret;
+
+       ret = register_chrdev_region(dev, IPATH_NMINORS, IPATH_DRV_NAME);
+       if (ret < 0) {
+               printk(KERN_ERR IPATH_DRV_NAME ": Could not register "
+                      "chrdev region (err %d)\n", -ret);
+               goto done;
+       }
+
+       ipath_class = class_create(THIS_MODULE, IPATH_DRV_NAME);
+
+       if (IS_ERR(ipath_class)) {
+               ret = PTR_ERR(ipath_class);
+               printk(KERN_ERR IPATH_DRV_NAME ": Could not create "
+                      "device class (err %d)\n", -ret);
+               goto bail;
+       }
+
+       goto done;
+bail:
+       unregister_chrdev_region(dev, IPATH_NMINORS);
+done:
+       return ret;
+}
+
+static void user_cleanup(void)
+{
+       if (ipath_class) {
+               class_destroy(ipath_class);
+               ipath_class = NULL;
+       }
+
+       unregister_chrdev_region(dev, IPATH_NMINORS);
+}
+
+static atomic_t user_count = ATOMIC_INIT(0);
+static atomic_t user_setup = ATOMIC_INIT(0);
+
+int ipath_user_add(struct ipath_devdata *dd)
+{
+       char name[10];
+       int ret;
+
+       if (atomic_inc_return(&user_count) == 1) {
+               ret = user_init();
+               if (ret < 0) {
+                       ipath_dev_err(dd, "Unable to set up user support: "
+                                     "error %d\n", -ret);
+                       goto bail;
+               }
+               ret = ipath_diag_init();
+               if (ret < 0) {
+                       ipath_dev_err(dd, "Unable to set up diag support: "
+                                     "error %d\n", -ret);
+                       goto bail_sma;
+               }
+
+               ret = init_cdev(0, "ipath", &ipath_file_ops, &wildcard_cdev,
+                               &wildcard_class_dev);
+               if (ret < 0) {
+                       ipath_dev_err(dd, "Could not create wildcard "
+                                     "minor: error %d\n", -ret);
+                       goto bail_diag;
+               }
+
+               atomic_set(&user_setup, 1);
+       }
+
+       snprintf(name, sizeof(name), "ipath%d", dd->ipath_unit);
+
+       ret = init_cdev(dd->ipath_unit + 1, name, &ipath_file_ops,
+                       &dd->cdev, &dd->class_dev);
+       if (ret < 0)
+               ipath_dev_err(dd, "Could not create user minor %d, %s\n",
+                             dd->ipath_unit + 1, name);
+
+       goto bail;
+
+bail_diag:
+       ipath_diag_cleanup();
+bail_sma:
+       user_cleanup();
+bail:
+       return ret;
+}
+
+void ipath_user_del(struct ipath_devdata *dd)
+{
+       cleanup_cdev(&dd->cdev, &dd->class_dev);
+
+       if (atomic_dec_return(&user_count) == 0) {
+               if (atomic_read(&user_setup) == 0)
+                       goto bail;
+
+               cleanup_cdev(&wildcard_cdev, &wildcard_class_dev);
+               ipath_diag_cleanup();
+               user_cleanup();
+
+               atomic_set(&user_setup, 0);
+       }
+bail:
+       return;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c
new file mode 100644 (file)
index 0000000..e274120
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+ * Copyright (c) 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/namei.h>
+#include <linux/pci.h>
+
+#include "ipath_kernel.h"
+
+#define IPATHFS_MAGIC 0x726a77
+
+static struct super_block *ipath_super;
+
+static int ipathfs_mknod(struct inode *dir, struct dentry *dentry,
+                        int mode, struct file_operations *fops,
+                        void *data)
+{
+       int error;
+       struct inode *inode = new_inode(dir->i_sb);
+
+       if (!inode) {
+               error = -EPERM;
+               goto bail;
+       }
+
+       inode->i_mode = mode;
+       inode->i_uid = 0;
+       inode->i_gid = 0;
+       inode->i_blksize = PAGE_CACHE_SIZE;
+       inode->i_blocks = 0;
+       inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+       inode->u.generic_ip = data;
+       if ((mode & S_IFMT) == S_IFDIR) {
+               inode->i_op = &simple_dir_inode_operations;
+               inode->i_nlink++;
+               dir->i_nlink++;
+       }
+
+       inode->i_fop = fops;
+
+       d_instantiate(dentry, inode);
+       error = 0;
+
+bail:
+       return error;
+}
+
+static int create_file(const char *name, mode_t mode,
+                      struct dentry *parent, struct dentry **dentry,
+                      struct file_operations *fops, void *data)
+{
+       int error;
+
+       *dentry = NULL;
+       mutex_lock(&parent->d_inode->i_mutex);
+       *dentry = lookup_one_len(name, parent, strlen(name));
+       if (!IS_ERR(dentry))
+               error = ipathfs_mknod(parent->d_inode, *dentry,
+                                     mode, fops, data);
+       else
+               error = PTR_ERR(dentry);
+       mutex_unlock(&parent->d_inode->i_mutex);
+
+       return error;
+}
+
+static ssize_t atomic_stats_read(struct file *file, char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+       return simple_read_from_buffer(buf, count, ppos, &ipath_stats,
+                                      sizeof ipath_stats);
+}
+
+static struct file_operations atomic_stats_ops = {
+       .read = atomic_stats_read,
+};
+
+#define NUM_COUNTERS sizeof(struct infinipath_counters) / sizeof(u64)
+
+static ssize_t atomic_counters_read(struct file *file, char __user *buf,
+                                   size_t count, loff_t *ppos)
+{
+       u64 counters[NUM_COUNTERS];
+       u16 i;
+       struct ipath_devdata *dd;
+
+       dd = file->f_dentry->d_inode->u.generic_ip;
+
+       for (i = 0; i < NUM_COUNTERS; i++)
+               counters[i] = ipath_snap_cntr(dd, i);
+
+       return simple_read_from_buffer(buf, count, ppos, counters,
+                                      sizeof counters);
+}
+
+static struct file_operations atomic_counters_ops = {
+       .read = atomic_counters_read,
+};
+
+static ssize_t atomic_node_info_read(struct file *file, char __user *buf,
+                                    size_t count, loff_t *ppos)
+{
+       u32 nodeinfo[10];
+       struct ipath_devdata *dd;
+       u64 guid;
+
+       dd = file->f_dentry->d_inode->u.generic_ip;
+
+       guid = be64_to_cpu(dd->ipath_guid);
+
+       nodeinfo[0] =                   /* BaseVersion is SMA */
+               /* ClassVersion is SMA */
+               (1 << 8)                /* NodeType  */
+               | (1 << 0);             /* NumPorts */
+       nodeinfo[1] = (u32) (guid >> 32);
+       nodeinfo[2] = (u32) (guid & 0xffffffff);
+       /* PortGUID == SystemImageGUID for us */
+       nodeinfo[3] = nodeinfo[1];
+       /* PortGUID == SystemImageGUID for us */
+       nodeinfo[4] = nodeinfo[2];
+       /* PortGUID == NodeGUID for us */
+       nodeinfo[5] = nodeinfo[3];
+       /* PortGUID == NodeGUID for us */
+       nodeinfo[6] = nodeinfo[4];
+       nodeinfo[7] = (4 << 16) /* we support 4 pkeys */
+               | (dd->ipath_deviceid << 0);
+       /* our chip version as 16 bits major, 16 bits minor */
+       nodeinfo[8] = dd->ipath_minrev | (dd->ipath_majrev << 16);
+       nodeinfo[9] = (dd->ipath_unit << 24) | (dd->ipath_vendorid << 0);
+
+       return simple_read_from_buffer(buf, count, ppos, nodeinfo,
+                                      sizeof nodeinfo);
+}
+
+static struct file_operations atomic_node_info_ops = {
+       .read = atomic_node_info_read,
+};
+
+static ssize_t atomic_port_info_read(struct file *file, char __user *buf,
+                                    size_t count, loff_t *ppos)
+{
+       u32 portinfo[13];
+       u32 tmp, tmp2;
+       struct ipath_devdata *dd;
+
+       dd = file->f_dentry->d_inode->u.generic_ip;
+
+       /* so we only initialize non-zero fields. */
+       memset(portinfo, 0, sizeof portinfo);
+
+       /*
+        * Notimpl yet M_Key (64)
+        * Notimpl yet GID (64)
+        */
+
+       portinfo[4] = (dd->ipath_lid << 16);
+
+       /*
+        * Notimpl yet SMLID (should we store this in the driver, in case
+        * SMA dies?)  CapabilityMask is 0, we don't support any of these
+        * DiagCode is 0; we don't store any diag info for now Notimpl yet
+        * M_KeyLeasePeriod (we don't support M_Key)
+        */
+
+       /* LocalPortNum is whichever port number they ask for */
+       portinfo[7] = (dd->ipath_unit << 24)
+               /* LinkWidthEnabled */
+               | (2 << 16)
+               /* LinkWidthSupported (really 2, but not IB valid) */
+               | (3 << 8)
+               /* LinkWidthActive */
+               | (2 << 0);
+       tmp = dd->ipath_lastibcstat & IPATH_IBSTATE_MASK;
+       tmp2 = 5;
+       if (tmp == IPATH_IBSTATE_INIT)
+               tmp = 2;
+       else if (tmp == IPATH_IBSTATE_ARM)
+               tmp = 3;
+       else if (tmp == IPATH_IBSTATE_ACTIVE)
+               tmp = 4;
+       else {
+               tmp = 0;        /* down */
+               tmp2 = tmp & 0xf;
+       }
+
+       portinfo[8] = (1 << 28) /* LinkSpeedSupported */
+               | (tmp << 24)   /* PortState */
+               | (tmp2 << 20)  /* PortPhysicalState */
+               | (2 << 16)
+
+               /* LinkDownDefaultState */
+               /* M_KeyProtectBits == 0 */
+               /* NotImpl yet LMC == 0 (we can support all values) */
+               | (1 << 4)      /* LinkSpeedActive */
+               | (1 << 0);     /* LinkSpeedEnabled */
+       switch (dd->ipath_ibmtu) {
+       case 4096:
+               tmp = 5;
+               break;
+       case 2048:
+               tmp = 4;
+               break;
+       case 1024:
+               tmp = 3;
+               break;
+       case 512:
+               tmp = 2;
+               break;
+       case 256:
+               tmp = 1;
+               break;
+       default:                /* oops, something is wrong */
+               ipath_dbg("Problem, ipath_ibmtu 0x%x not a valid IB MTU, "
+                         "treat as 2048\n", dd->ipath_ibmtu);
+               tmp = 4;
+               break;
+       }
+       portinfo[9] = (tmp << 28)
+               /* NeighborMTU */
+               /* Notimpl MasterSMSL */
+               | (1 << 20)
+
+               /* VLCap */
+               /* Notimpl InitType (actually, an SMA decision) */
+               /* VLHighLimit is 0 (only one VL) */
+               ; /* VLArbitrationHighCap is 0 (only one VL) */
+       portinfo[10] =  /* VLArbitrationLowCap is 0 (only one VL) */
+               /* InitTypeReply is SMA decision */
+               (5 << 16)       /* MTUCap 4096 */
+               | (7 << 13)     /* VLStallCount */
+               | (0x1f << 8)   /* HOQLife */
+               | (1 << 4)
+
+               /* OperationalVLs 0 */
+               /* PartitionEnforcementInbound */
+               /* PartitionEnforcementOutbound not enforced */
+               /* FilterRawinbound not enforced */
+               ;               /* FilterRawOutbound not enforced */
+       /* M_KeyViolations are not counted by hardware, SMA can count */
+       tmp = ipath_read_creg32(dd, dd->ipath_cregs->cr_errpkey);
+       /* P_KeyViolations are counted by hardware. */
+       portinfo[11] = ((tmp & 0xffff) << 0);
+       portinfo[12] =
+               /* Q_KeyViolations are not counted by hardware */
+               (1 << 8)
+
+               /* GUIDCap */
+               /* SubnetTimeOut handled by SMA */
+               /* RespTimeValue handled by SMA */
+               ;
+       /* LocalPhyErrors are programmed to max */
+       portinfo[12] |= (0xf << 20)
+               | (0xf << 16)   /* OverRunErrors are programmed to max */
+               ;
+
+       return simple_read_from_buffer(buf, count, ppos, portinfo,
+                                      sizeof portinfo);
+}
+
+static struct file_operations atomic_port_info_ops = {
+       .read = atomic_port_info_read,
+};
+
+static ssize_t flash_read(struct file *file, char __user *buf,
+                         size_t count, loff_t *ppos)
+{
+       struct ipath_devdata *dd;
+       ssize_t ret;
+       loff_t pos;
+       char *tmp;
+
+       pos = *ppos;
+
+       if ( pos < 0) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       if (pos >= sizeof(struct ipath_flash)) {
+               ret = 0;
+               goto bail;
+       }
+
+       if (count > sizeof(struct ipath_flash) - pos)
+               count = sizeof(struct ipath_flash) - pos;
+
+       tmp = kmalloc(count, GFP_KERNEL);
+       if (!tmp) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+
+       dd = file->f_dentry->d_inode->u.generic_ip;
+       if (ipath_eeprom_read(dd, pos, tmp, count)) {
+               ipath_dev_err(dd, "failed to read from flash\n");
+               ret = -ENXIO;
+               goto bail_tmp;
+       }
+
+       if (copy_to_user(buf, tmp, count)) {
+               ret = -EFAULT;
+               goto bail_tmp;
+       }
+
+       *ppos = pos + count;
+       ret = count;
+
+bail_tmp:
+       kfree(tmp);
+
+bail:
+       return ret;
+}
+
+static ssize_t flash_write(struct file *file, const char __user *buf,
+                          size_t count, loff_t *ppos)
+{
+       struct ipath_devdata *dd;
+       ssize_t ret;
+       loff_t pos;
+       char *tmp;
+
+       pos = *ppos;
+
+       if ( pos < 0) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       if (pos >= sizeof(struct ipath_flash)) {
+               ret = 0;
+               goto bail;
+       }
+
+       if (count > sizeof(struct ipath_flash) - pos)
+               count = sizeof(struct ipath_flash) - pos;
+
+       tmp = kmalloc(count, GFP_KERNEL);
+       if (!tmp) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+
+       if (copy_from_user(tmp, buf, count)) {
+               ret = -EFAULT;
+               goto bail_tmp;
+       }
+
+       dd = file->f_dentry->d_inode->u.generic_ip;
+       if (ipath_eeprom_write(dd, pos, tmp, count)) {
+               ret = -ENXIO;
+               ipath_dev_err(dd, "failed to write to flash\n");
+               goto bail_tmp;
+       }
+
+       *ppos = pos + count;
+       ret = count;
+
+bail_tmp:
+       kfree(tmp);
+
+bail:
+       return ret;
+}
+
+static struct file_operations flash_ops = {
+       .read = flash_read,
+       .write = flash_write,
+};
+
+static int create_device_files(struct super_block *sb,
+                              struct ipath_devdata *dd)
+{
+       struct dentry *dir, *tmp;
+       char unit[10];
+       int ret;
+
+       snprintf(unit, sizeof unit, "%02d", dd->ipath_unit);
+       ret = create_file(unit, S_IFDIR|S_IRUGO|S_IXUGO, sb->s_root, &dir,
+                         (struct file_operations *) &simple_dir_operations,
+                         dd);
+       if (ret) {
+               printk(KERN_ERR "create_file(%s) failed: %d\n", unit, ret);
+               goto bail;
+       }
+
+       ret = create_file("atomic_counters", S_IFREG|S_IRUGO, dir, &tmp,
+                         &atomic_counters_ops, dd);
+       if (ret) {
+               printk(KERN_ERR "create_file(%s/atomic_counters) "
+                      "failed: %d\n", unit, ret);
+               goto bail;
+       }
+
+       ret = create_file("node_info", S_IFREG|S_IRUGO, dir, &tmp,
+                         &atomic_node_info_ops, dd);
+       if (ret) {
+               printk(KERN_ERR "create_file(%s/node_info) "
+                      "failed: %d\n", unit, ret);
+               goto bail;
+       }
+
+       ret = create_file("port_info", S_IFREG|S_IRUGO, dir, &tmp,
+                         &atomic_port_info_ops, dd);
+       if (ret) {
+               printk(KERN_ERR "create_file(%s/port_info) "
+                      "failed: %d\n", unit, ret);
+               goto bail;
+       }
+
+       ret = create_file("flash", S_IFREG|S_IWUSR|S_IRUGO, dir, &tmp,
+                         &flash_ops, dd);
+       if (ret) {
+               printk(KERN_ERR "create_file(%s/flash) "
+                      "failed: %d\n", unit, ret);
+               goto bail;
+       }
+
+bail:
+       return ret;
+}
+
+static void remove_file(struct dentry *parent, char *name)
+{
+       struct dentry *tmp;
+
+       tmp = lookup_one_len(name, parent, strlen(name));
+
+       spin_lock(&dcache_lock);
+       spin_lock(&tmp->d_lock);
+       if (!(d_unhashed(tmp) && tmp->d_inode)) {
+               dget_locked(tmp);
+               __d_drop(tmp);
+               spin_unlock(&tmp->d_lock);
+               spin_unlock(&dcache_lock);
+               simple_unlink(parent->d_inode, tmp);
+       } else {
+               spin_unlock(&tmp->d_lock);
+               spin_unlock(&dcache_lock);
+       }
+}
+
+static int remove_device_files(struct super_block *sb,
+                              struct ipath_devdata *dd)
+{
+       struct dentry *dir, *root;
+       char unit[10];
+       int ret;
+
+       root = dget(sb->s_root);
+       mutex_lock(&root->d_inode->i_mutex);
+       snprintf(unit, sizeof unit, "%02d", dd->ipath_unit);
+       dir = lookup_one_len(unit, root, strlen(unit));
+
+       if (IS_ERR(dir)) {
+               ret = PTR_ERR(dir);
+               printk(KERN_ERR "Lookup of %s failed\n", unit);
+               goto bail;
+       }
+
+       remove_file(dir, "flash");
+       remove_file(dir, "port_info");
+       remove_file(dir, "node_info");
+       remove_file(dir, "atomic_counters");
+       d_delete(dir);
+       ret = simple_rmdir(root->d_inode, dir);
+
+bail:
+       mutex_unlock(&root->d_inode->i_mutex);
+       dput(root);
+       return ret;
+}
+
+static int ipathfs_fill_super(struct super_block *sb, void *data,
+                             int silent)
+{
+       struct ipath_devdata *dd, *tmp;
+       unsigned long flags;
+       int ret;
+
+       static struct tree_descr files[] = {
+               [1] = {"atomic_stats", &atomic_stats_ops, S_IRUGO},
+               {""},
+       };
+
+       ret = simple_fill_super(sb, IPATHFS_MAGIC, files);
+       if (ret) {
+               printk(KERN_ERR "simple_fill_super failed: %d\n", ret);
+               goto bail;
+       }
+
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+
+       list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) {
+               spin_unlock_irqrestore(&ipath_devs_lock, flags);
+               ret = create_device_files(sb, dd);
+               if (ret) {
+                       deactivate_super(sb);
+                       goto bail;
+               }
+               spin_lock_irqsave(&ipath_devs_lock, flags);
+       }
+
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+
+bail:
+       return ret;
+}
+
+static struct super_block *ipathfs_get_sb(struct file_system_type *fs_type,
+                                       int flags, const char *dev_name,
+                                       void *data)
+{
+       ipath_super = get_sb_single(fs_type, flags, data,
+                                   ipathfs_fill_super);
+       return ipath_super;
+}
+
+static void ipathfs_kill_super(struct super_block *s)
+{
+       kill_litter_super(s);
+       ipath_super = NULL;
+}
+
+int ipathfs_add_device(struct ipath_devdata *dd)
+{
+       int ret;
+
+       if (ipath_super == NULL) {
+               ret = 0;
+               goto bail;
+       }
+
+       ret = create_device_files(ipath_super, dd);
+
+bail:
+       return ret;
+}
+
+int ipathfs_remove_device(struct ipath_devdata *dd)
+{
+       int ret;
+
+       if (ipath_super == NULL) {
+               ret = 0;
+               goto bail;
+       }
+
+       ret = remove_device_files(ipath_super, dd);
+
+bail:
+       return ret;
+}
+
+static struct file_system_type ipathfs_fs_type = {
+       .owner =        THIS_MODULE,
+       .name =         "ipathfs",
+       .get_sb =       ipathfs_get_sb,
+       .kill_sb =      ipathfs_kill_super,
+};
+
+int __init ipath_init_ipathfs(void)
+{
+       return register_filesystem(&ipathfs_fs_type);
+}
+
+void __exit ipath_exit_ipathfs(void)
+{
+       unregister_filesystem(&ipathfs_fs_type);
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_ht400.c b/drivers/infiniband/hw/ipath/ipath_ht400.c
new file mode 100644 (file)
index 0000000..4652435
--- /dev/null
@@ -0,0 +1,1586 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * This file contains all of the code that is specific to the InfiniPath
+ * HT-400 chip.
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "ipath_kernel.h"
+#include "ipath_registers.h"
+
+/*
+ * This lists the InfiniPath HT400 registers, in the actual chip layout.
+ * This structure should never be directly accessed.
+ *
+ * The names are in InterCap form because they're taken straight from
+ * the chip specification.  Since they're only used in this file, they
+ * don't pollute the rest of the source.
+*/
+
+struct _infinipath_do_not_use_kernel_regs {
+       unsigned long long Revision;
+       unsigned long long Control;
+       unsigned long long PageAlign;
+       unsigned long long PortCnt;
+       unsigned long long DebugPortSelect;
+       unsigned long long DebugPort;
+       unsigned long long SendRegBase;
+       unsigned long long UserRegBase;
+       unsigned long long CounterRegBase;
+       unsigned long long Scratch;
+       unsigned long long ReservedMisc1;
+       unsigned long long InterruptConfig;
+       unsigned long long IntBlocked;
+       unsigned long long IntMask;
+       unsigned long long IntStatus;
+       unsigned long long IntClear;
+       unsigned long long ErrorMask;
+       unsigned long long ErrorStatus;
+       unsigned long long ErrorClear;
+       unsigned long long HwErrMask;
+       unsigned long long HwErrStatus;
+       unsigned long long HwErrClear;
+       unsigned long long HwDiagCtrl;
+       unsigned long long MDIO;
+       unsigned long long IBCStatus;
+       unsigned long long IBCCtrl;
+       unsigned long long ExtStatus;
+       unsigned long long ExtCtrl;
+       unsigned long long GPIOOut;
+       unsigned long long GPIOMask;
+       unsigned long long GPIOStatus;
+       unsigned long long GPIOClear;
+       unsigned long long RcvCtrl;
+       unsigned long long RcvBTHQP;
+       unsigned long long RcvHdrSize;
+       unsigned long long RcvHdrCnt;
+       unsigned long long RcvHdrEntSize;
+       unsigned long long RcvTIDBase;
+       unsigned long long RcvTIDCnt;
+       unsigned long long RcvEgrBase;
+       unsigned long long RcvEgrCnt;
+       unsigned long long RcvBufBase;
+       unsigned long long RcvBufSize;
+       unsigned long long RxIntMemBase;
+       unsigned long long RxIntMemSize;
+       unsigned long long RcvPartitionKey;
+       unsigned long long ReservedRcv[10];
+       unsigned long long SendCtrl;
+       unsigned long long SendPIOBufBase;
+       unsigned long long SendPIOSize;
+       unsigned long long SendPIOBufCnt;
+       unsigned long long SendPIOAvailAddr;
+       unsigned long long TxIntMemBase;
+       unsigned long long TxIntMemSize;
+       unsigned long long ReservedSend[9];
+       unsigned long long SendBufferError;
+       unsigned long long SendBufferErrorCONT1;
+       unsigned long long SendBufferErrorCONT2;
+       unsigned long long SendBufferErrorCONT3;
+       unsigned long long ReservedSBE[4];
+       unsigned long long RcvHdrAddr0;
+       unsigned long long RcvHdrAddr1;
+       unsigned long long RcvHdrAddr2;
+       unsigned long long RcvHdrAddr3;
+       unsigned long long RcvHdrAddr4;
+       unsigned long long RcvHdrAddr5;
+       unsigned long long RcvHdrAddr6;
+       unsigned long long RcvHdrAddr7;
+       unsigned long long RcvHdrAddr8;
+       unsigned long long ReservedRHA[7];
+       unsigned long long RcvHdrTailAddr0;
+       unsigned long long RcvHdrTailAddr1;
+       unsigned long long RcvHdrTailAddr2;
+       unsigned long long RcvHdrTailAddr3;
+       unsigned long long RcvHdrTailAddr4;
+       unsigned long long RcvHdrTailAddr5;
+       unsigned long long RcvHdrTailAddr6;
+       unsigned long long RcvHdrTailAddr7;
+       unsigned long long RcvHdrTailAddr8;
+       unsigned long long ReservedRHTA[7];
+       unsigned long long Sync;        /* Software only */
+       unsigned long long Dump;        /* Software only */
+       unsigned long long SimVer;      /* Software only */
+       unsigned long long ReservedSW[5];
+       unsigned long long SerdesConfig0;
+       unsigned long long SerdesConfig1;
+       unsigned long long SerdesStatus;
+       unsigned long long XGXSConfig;
+       unsigned long long ReservedSW2[4];
+};
+
+#define IPATH_KREG_OFFSET(field) (offsetof(struct \
+    _infinipath_do_not_use_kernel_regs, field) / sizeof(u64))
+#define IPATH_CREG_OFFSET(field) (offsetof( \
+    struct infinipath_counters, field) / sizeof(u64))
+
+static const struct ipath_kregs ipath_ht_kregs = {
+       .kr_control = IPATH_KREG_OFFSET(Control),
+       .kr_counterregbase = IPATH_KREG_OFFSET(CounterRegBase),
+       .kr_debugport = IPATH_KREG_OFFSET(DebugPort),
+       .kr_debugportselect = IPATH_KREG_OFFSET(DebugPortSelect),
+       .kr_errorclear = IPATH_KREG_OFFSET(ErrorClear),
+       .kr_errormask = IPATH_KREG_OFFSET(ErrorMask),
+       .kr_errorstatus = IPATH_KREG_OFFSET(ErrorStatus),
+       .kr_extctrl = IPATH_KREG_OFFSET(ExtCtrl),
+       .kr_extstatus = IPATH_KREG_OFFSET(ExtStatus),
+       .kr_gpio_clear = IPATH_KREG_OFFSET(GPIOClear),
+       .kr_gpio_mask = IPATH_KREG_OFFSET(GPIOMask),
+       .kr_gpio_out = IPATH_KREG_OFFSET(GPIOOut),
+       .kr_gpio_status = IPATH_KREG_OFFSET(GPIOStatus),
+       .kr_hwdiagctrl = IPATH_KREG_OFFSET(HwDiagCtrl),
+       .kr_hwerrclear = IPATH_KREG_OFFSET(HwErrClear),
+       .kr_hwerrmask = IPATH_KREG_OFFSET(HwErrMask),
+       .kr_hwerrstatus = IPATH_KREG_OFFSET(HwErrStatus),
+       .kr_ibcctrl = IPATH_KREG_OFFSET(IBCCtrl),
+       .kr_ibcstatus = IPATH_KREG_OFFSET(IBCStatus),
+       .kr_intblocked = IPATH_KREG_OFFSET(IntBlocked),
+       .kr_intclear = IPATH_KREG_OFFSET(IntClear),
+       .kr_interruptconfig = IPATH_KREG_OFFSET(InterruptConfig),
+       .kr_intmask = IPATH_KREG_OFFSET(IntMask),
+       .kr_intstatus = IPATH_KREG_OFFSET(IntStatus),
+       .kr_mdio = IPATH_KREG_OFFSET(MDIO),
+       .kr_pagealign = IPATH_KREG_OFFSET(PageAlign),
+       .kr_partitionkey = IPATH_KREG_OFFSET(RcvPartitionKey),
+       .kr_portcnt = IPATH_KREG_OFFSET(PortCnt),
+       .kr_rcvbthqp = IPATH_KREG_OFFSET(RcvBTHQP),
+       .kr_rcvbufbase = IPATH_KREG_OFFSET(RcvBufBase),
+       .kr_rcvbufsize = IPATH_KREG_OFFSET(RcvBufSize),
+       .kr_rcvctrl = IPATH_KREG_OFFSET(RcvCtrl),
+       .kr_rcvegrbase = IPATH_KREG_OFFSET(RcvEgrBase),
+       .kr_rcvegrcnt = IPATH_KREG_OFFSET(RcvEgrCnt),
+       .kr_rcvhdrcnt = IPATH_KREG_OFFSET(RcvHdrCnt),
+       .kr_rcvhdrentsize = IPATH_KREG_OFFSET(RcvHdrEntSize),
+       .kr_rcvhdrsize = IPATH_KREG_OFFSET(RcvHdrSize),
+       .kr_rcvintmembase = IPATH_KREG_OFFSET(RxIntMemBase),
+       .kr_rcvintmemsize = IPATH_KREG_OFFSET(RxIntMemSize),
+       .kr_rcvtidbase = IPATH_KREG_OFFSET(RcvTIDBase),
+       .kr_rcvtidcnt = IPATH_KREG_OFFSET(RcvTIDCnt),
+       .kr_revision = IPATH_KREG_OFFSET(Revision),
+       .kr_scratch = IPATH_KREG_OFFSET(Scratch),
+       .kr_sendbuffererror = IPATH_KREG_OFFSET(SendBufferError),
+       .kr_sendctrl = IPATH_KREG_OFFSET(SendCtrl),
+       .kr_sendpioavailaddr = IPATH_KREG_OFFSET(SendPIOAvailAddr),
+       .kr_sendpiobufbase = IPATH_KREG_OFFSET(SendPIOBufBase),
+       .kr_sendpiobufcnt = IPATH_KREG_OFFSET(SendPIOBufCnt),
+       .kr_sendpiosize = IPATH_KREG_OFFSET(SendPIOSize),
+       .kr_sendregbase = IPATH_KREG_OFFSET(SendRegBase),
+       .kr_txintmembase = IPATH_KREG_OFFSET(TxIntMemBase),
+       .kr_txintmemsize = IPATH_KREG_OFFSET(TxIntMemSize),
+       .kr_userregbase = IPATH_KREG_OFFSET(UserRegBase),
+       .kr_serdesconfig0 = IPATH_KREG_OFFSET(SerdesConfig0),
+       .kr_serdesconfig1 = IPATH_KREG_OFFSET(SerdesConfig1),
+       .kr_serdesstatus = IPATH_KREG_OFFSET(SerdesStatus),
+       .kr_xgxsconfig = IPATH_KREG_OFFSET(XGXSConfig),
+       /*
+        * These should not be used directly via ipath_read_kreg64(),
+        * use them with ipath_read_kreg64_port(),
+        */
+       .kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0),
+       .kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0)
+};
+
+static const struct ipath_cregs ipath_ht_cregs = {
+       .cr_badformatcnt = IPATH_CREG_OFFSET(RxBadFormatCnt),
+       .cr_erricrccnt = IPATH_CREG_OFFSET(RxICRCErrCnt),
+       .cr_errlinkcnt = IPATH_CREG_OFFSET(RxLinkProblemCnt),
+       .cr_errlpcrccnt = IPATH_CREG_OFFSET(RxLPCRCErrCnt),
+       .cr_errpkey = IPATH_CREG_OFFSET(RxPKeyMismatchCnt),
+       .cr_errrcvflowctrlcnt = IPATH_CREG_OFFSET(RxFlowCtrlErrCnt),
+       .cr_err_rlencnt = IPATH_CREG_OFFSET(RxLenErrCnt),
+       .cr_errslencnt = IPATH_CREG_OFFSET(TxLenErrCnt),
+       .cr_errtidfull = IPATH_CREG_OFFSET(RxTIDFullErrCnt),
+       .cr_errtidvalid = IPATH_CREG_OFFSET(RxTIDValidErrCnt),
+       .cr_errvcrccnt = IPATH_CREG_OFFSET(RxVCRCErrCnt),
+       .cr_ibstatuschange = IPATH_CREG_OFFSET(IBStatusChangeCnt),
+       /* calc from Reg_CounterRegBase + offset */
+       .cr_intcnt = IPATH_CREG_OFFSET(LBIntCnt),
+       .cr_invalidrlencnt = IPATH_CREG_OFFSET(RxMaxMinLenErrCnt),
+       .cr_invalidslencnt = IPATH_CREG_OFFSET(TxMaxMinLenErrCnt),
+       .cr_lbflowstallcnt = IPATH_CREG_OFFSET(LBFlowStallCnt),
+       .cr_pktrcvcnt = IPATH_CREG_OFFSET(RxDataPktCnt),
+       .cr_pktrcvflowctrlcnt = IPATH_CREG_OFFSET(RxFlowPktCnt),
+       .cr_pktsendcnt = IPATH_CREG_OFFSET(TxDataPktCnt),
+       .cr_pktsendflowcnt = IPATH_CREG_OFFSET(TxFlowPktCnt),
+       .cr_portovflcnt = IPATH_CREG_OFFSET(RxP0HdrEgrOvflCnt),
+       .cr_rcvebpcnt = IPATH_CREG_OFFSET(RxEBPCnt),
+       .cr_rcvovflcnt = IPATH_CREG_OFFSET(RxBufOvflCnt),
+       .cr_senddropped = IPATH_CREG_OFFSET(TxDroppedPktCnt),
+       .cr_sendstallcnt = IPATH_CREG_OFFSET(TxFlowStallCnt),
+       .cr_sendunderruncnt = IPATH_CREG_OFFSET(TxUnderrunCnt),
+       .cr_wordrcvcnt = IPATH_CREG_OFFSET(RxDwordCnt),
+       .cr_wordsendcnt = IPATH_CREG_OFFSET(TxDwordCnt),
+       .cr_unsupvlcnt = IPATH_CREG_OFFSET(TxUnsupVLErrCnt),
+       .cr_rxdroppktcnt = IPATH_CREG_OFFSET(RxDroppedPktCnt),
+       .cr_iblinkerrrecovcnt = IPATH_CREG_OFFSET(IBLinkErrRecoveryCnt),
+       .cr_iblinkdowncnt = IPATH_CREG_OFFSET(IBLinkDownedCnt),
+       .cr_ibsymbolerrcnt = IPATH_CREG_OFFSET(IBSymbolErrCnt)
+};
+
+/* kr_intstatus, kr_intclear, kr_intmask bits */
+#define INFINIPATH_I_RCVURG_MASK 0x1FF
+#define INFINIPATH_I_RCVAVAIL_MASK 0x1FF
+
+/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
+#define INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT 0
+#define INFINIPATH_HWE_HTCMEMPARITYERR_MASK 0x3FFFFFULL
+#define INFINIPATH_HWE_HTCLNKABYTE0CRCERR   0x0000000000800000ULL
+#define INFINIPATH_HWE_HTCLNKABYTE1CRCERR   0x0000000001000000ULL
+#define INFINIPATH_HWE_HTCLNKBBYTE0CRCERR   0x0000000002000000ULL
+#define INFINIPATH_HWE_HTCLNKBBYTE1CRCERR   0x0000000004000000ULL
+#define INFINIPATH_HWE_HTCMISCERR4          0x0000000008000000ULL
+#define INFINIPATH_HWE_HTCMISCERR5          0x0000000010000000ULL
+#define INFINIPATH_HWE_HTCMISCERR6          0x0000000020000000ULL
+#define INFINIPATH_HWE_HTCMISCERR7          0x0000000040000000ULL
+#define INFINIPATH_HWE_HTCBUSTREQPARITYERR  0x0000000080000000ULL
+#define INFINIPATH_HWE_HTCBUSTRESPPARITYERR 0x0000000100000000ULL
+#define INFINIPATH_HWE_HTCBUSIREQPARITYERR  0x0000000200000000ULL
+#define INFINIPATH_HWE_COREPLL_FBSLIP       0x0080000000000000ULL
+#define INFINIPATH_HWE_COREPLL_RFSLIP       0x0100000000000000ULL
+#define INFINIPATH_HWE_HTBPLL_FBSLIP        0x0200000000000000ULL
+#define INFINIPATH_HWE_HTBPLL_RFSLIP        0x0400000000000000ULL
+#define INFINIPATH_HWE_HTAPLL_FBSLIP        0x0800000000000000ULL
+#define INFINIPATH_HWE_HTAPLL_RFSLIP        0x1000000000000000ULL
+#define INFINIPATH_HWE_SERDESPLLFAILED      0x2000000000000000ULL
+
+/* kr_extstatus bits */
+#define INFINIPATH_EXTS_FREQSEL 0x2
+#define INFINIPATH_EXTS_SERDESSEL 0x4
+#define INFINIPATH_EXTS_MEMBIST_ENDTEST     0x0000000000004000
+#define INFINIPATH_EXTS_MEMBIST_CORRECT     0x0000000000008000
+
+/*
+ * masks and bits that are different in different chips, or present only
+ * in one
+ */
+static const ipath_err_t infinipath_hwe_htcmemparityerr_mask =
+    INFINIPATH_HWE_HTCMEMPARITYERR_MASK;
+static const ipath_err_t infinipath_hwe_htcmemparityerr_shift =
+    INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT;
+
+static const ipath_err_t infinipath_hwe_htclnkabyte0crcerr =
+    INFINIPATH_HWE_HTCLNKABYTE0CRCERR;
+static const ipath_err_t infinipath_hwe_htclnkabyte1crcerr =
+    INFINIPATH_HWE_HTCLNKABYTE1CRCERR;
+static const ipath_err_t infinipath_hwe_htclnkbbyte0crcerr =
+    INFINIPATH_HWE_HTCLNKBBYTE0CRCERR;
+static const ipath_err_t infinipath_hwe_htclnkbbyte1crcerr =
+    INFINIPATH_HWE_HTCLNKBBYTE1CRCERR;
+
+#define _IPATH_GPIO_SDA_NUM 1
+#define _IPATH_GPIO_SCL_NUM 0
+
+#define IPATH_GPIO_SDA \
+       (1ULL << (_IPATH_GPIO_SDA_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT))
+#define IPATH_GPIO_SCL \
+       (1ULL << (_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT))
+
+/* keep the code below somewhat more readonable; not used elsewhere */
+#define _IPATH_HTLINK0_CRCBITS (infinipath_hwe_htclnkabyte0crcerr |    \
+                               infinipath_hwe_htclnkabyte1crcerr)
+#define _IPATH_HTLINK1_CRCBITS (infinipath_hwe_htclnkbbyte0crcerr |    \
+                               infinipath_hwe_htclnkbbyte1crcerr)
+#define _IPATH_HTLANE0_CRCBITS (infinipath_hwe_htclnkabyte0crcerr |    \
+                               infinipath_hwe_htclnkbbyte0crcerr)
+#define _IPATH_HTLANE1_CRCBITS (infinipath_hwe_htclnkabyte1crcerr |    \
+                               infinipath_hwe_htclnkbbyte1crcerr)
+
+static void hwerr_crcbits(struct ipath_devdata *dd, ipath_err_t hwerrs,
+                         char *msg, size_t msgl)
+{
+       char bitsmsg[64];
+       ipath_err_t crcbits = hwerrs &
+               (_IPATH_HTLINK0_CRCBITS | _IPATH_HTLINK1_CRCBITS);
+       /* don't check if 8bit HT */
+       if (dd->ipath_flags & IPATH_8BIT_IN_HT0)
+               crcbits &= ~infinipath_hwe_htclnkabyte1crcerr;
+       /* don't check if 8bit HT */
+       if (dd->ipath_flags & IPATH_8BIT_IN_HT1)
+               crcbits &= ~infinipath_hwe_htclnkbbyte1crcerr;
+       /*
+        * we'll want to ignore link errors on link that is
+        * not in use, if any.  For now, complain about both
+        */
+       if (crcbits) {
+               u16 ctrl0, ctrl1;
+               snprintf(bitsmsg, sizeof bitsmsg,
+                        "[HT%s lane %s CRC (%llx); ignore till reload]",
+                        !(crcbits & _IPATH_HTLINK1_CRCBITS) ?
+                        "0 (A)" : (!(crcbits & _IPATH_HTLINK0_CRCBITS)
+                                   ? "1 (B)" : "0+1 (A+B)"),
+                        !(crcbits & _IPATH_HTLANE1_CRCBITS) ? "0"
+                        : (!(crcbits & _IPATH_HTLANE0_CRCBITS) ? "1" :
+                           "0+1"), (unsigned long long) crcbits);
+               strlcat(msg, bitsmsg, msgl);
+
+               /*
+                * print extra info for debugging.  slave/primary
+                * config word 4, 8 (link control 0, 1)
+                */
+
+               if (pci_read_config_word(dd->pcidev,
+                                        dd->ipath_ht_slave_off + 0x4,
+                                        &ctrl0))
+                       dev_info(&dd->pcidev->dev, "Couldn't read "
+                                "linkctrl0 of slave/primary "
+                                "config block\n");
+               else if (!(ctrl0 & 1 << 6))
+                       /* not if EOC bit set */
+                       ipath_dbg("HT linkctrl0 0x%x%s%s\n", ctrl0,
+                                 ((ctrl0 >> 8) & 7) ? " CRC" : "",
+                                 ((ctrl0 >> 4) & 1) ? "linkfail" :
+                                 "");
+               if (pci_read_config_word(dd->pcidev,
+                                        dd->ipath_ht_slave_off + 0x8,
+                                        &ctrl1))
+                       dev_info(&dd->pcidev->dev, "Couldn't read "
+                                "linkctrl1 of slave/primary "
+                                "config block\n");
+               else if (!(ctrl1 & 1 << 6))
+                       /* not if EOC bit set */
+                       ipath_dbg("HT linkctrl1 0x%x%s%s\n", ctrl1,
+                                 ((ctrl1 >> 8) & 7) ? " CRC" : "",
+                                 ((ctrl1 >> 4) & 1) ? "linkfail" :
+                                 "");
+
+               /* disable until driver reloaded */
+               dd->ipath_hwerrmask &= ~crcbits;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask,
+                                dd->ipath_hwerrmask);
+               ipath_dbg("HT crc errs: %s\n", msg);
+       } else
+               ipath_dbg("ignoring HT crc errors 0x%llx, "
+                         "not in use\n", (unsigned long long)
+                         (hwerrs & (_IPATH_HTLINK0_CRCBITS |
+                                    _IPATH_HTLINK1_CRCBITS)));
+}
+
+/**
+ * ipath_ht_handle_hwerrors - display hardware errors
+ * @dd: the infinipath device
+ * @msg: the output buffer
+ * @msgl: the size of the output buffer
+ *
+ * Use same msg buffer as regular errors to avoid
+ * excessive stack use.  Most hardware errors are catastrophic, but for
+ * right now, we'll print them and continue.
+ * We reuse the same message buffer as ipath_handle_errors() to avoid
+ * excessive stack usage.
+ */
+static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
+                                    size_t msgl)
+{
+       ipath_err_t hwerrs;
+       u32 bits, ctrl;
+       int isfatal = 0;
+       char bitsmsg[64];
+
+       hwerrs = ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwerrstatus);
+
+       if (!hwerrs) {
+               ipath_cdbg(VERBOSE, "Called but no hardware errors set\n");
+               /*
+                * better than printing cofusing messages
+                * This seems to be related to clearing the crc error, or
+                * the pll error during init.
+                */
+               goto bail;
+       } else if (hwerrs == -1LL) {
+               ipath_dev_err(dd, "Read of hardware error status failed "
+                             "(all bits set); ignoring\n");
+               goto bail;
+       }
+       ipath_stats.sps_hwerrs++;
+
+       /* Always clear the error status register, except MEMBISTFAIL,
+        * regardless of whether we continue or stop using the chip.
+        * We want that set so we know it failed, even across driver reload.
+        * We'll still ignore it in the hwerrmask.  We do this partly for
+        * diagnostics, but also for support */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear,
+                        hwerrs&~INFINIPATH_HWE_MEMBISTFAILED);
+
+       hwerrs &= dd->ipath_hwerrmask;
+
+       /*
+        * make sure we get this much out, unless told to be quiet,
+        * or it's occurred within the last 5 seconds
+        */
+       if ((hwerrs & ~dd->ipath_lasthwerror) ||
+           (ipath_debug & __IPATH_VERBDBG))
+               dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx "
+                        "(cleared)\n", (unsigned long long) hwerrs);
+       dd->ipath_lasthwerror |= hwerrs;
+
+       if (hwerrs & ~infinipath_hwe_bitsextant)
+               ipath_dev_err(dd, "hwerror interrupt with unknown errors "
+                             "%llx set\n", (unsigned long long)
+                             (hwerrs & ~infinipath_hwe_bitsextant));
+
+       ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control);
+       if (ctrl & INFINIPATH_C_FREEZEMODE) {
+               if (hwerrs) {
+                       /*
+                        * if any set that we aren't ignoring; only
+                        * make the complaint once, in case it's stuck
+                        * or recurring, and we get here multiple
+                        * times.
+                        */
+                       if (dd->ipath_flags & IPATH_INITTED) {
+                               ipath_dev_err(dd, "Fatal Error (freeze "
+                                             "mode), no longer usable\n");
+                               isfatal = 1;
+                       }
+                       *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
+                       /* mark as having had error */
+                       *dd->ipath_statusp |= IPATH_STATUS_HWERROR;
+                       /*
+                        * mark as not usable, at a minimum until driver
+                        * is reloaded, probably until reboot, since no
+                        * other reset is possible.
+                        */
+                       dd->ipath_flags &= ~IPATH_INITTED;
+               } else {
+                       ipath_dbg("Clearing freezemode on ignored hardware "
+                                 "error\n");
+                       ctrl &= ~INFINIPATH_C_FREEZEMODE;
+                       ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
+                                        ctrl);
+               }
+       }
+
+       *msg = '\0';
+
+       /*
+        * may someday want to decode into which bits are which
+        * functional area for parity errors, etc.
+        */
+       if (hwerrs & (infinipath_hwe_htcmemparityerr_mask
+                     << INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT)) {
+               bits = (u32) ((hwerrs >>
+                              INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT) &
+                             INFINIPATH_HWE_HTCMEMPARITYERR_MASK);
+               snprintf(bitsmsg, sizeof bitsmsg, "[HTC Parity Errs %x] ",
+                        bits);
+               strlcat(msg, bitsmsg, msgl);
+       }
+       if (hwerrs & (INFINIPATH_HWE_RXEMEMPARITYERR_MASK
+                     << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)) {
+               bits = (u32) ((hwerrs >>
+                              INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) &
+                             INFINIPATH_HWE_RXEMEMPARITYERR_MASK);
+               snprintf(bitsmsg, sizeof bitsmsg, "[RXE Parity Errs %x] ",
+                        bits);
+               strlcat(msg, bitsmsg, msgl);
+       }
+       if (hwerrs & (INFINIPATH_HWE_TXEMEMPARITYERR_MASK
+                     << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
+               bits = (u32) ((hwerrs >>
+                              INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) &
+                             INFINIPATH_HWE_TXEMEMPARITYERR_MASK);
+               snprintf(bitsmsg, sizeof bitsmsg, "[TXE Parity Errs %x] ",
+                        bits);
+               strlcat(msg, bitsmsg, msgl);
+       }
+       if (hwerrs & INFINIPATH_HWE_IBCBUSTOSPCPARITYERR)
+               strlcat(msg, "[IB2IPATH Parity]", msgl);
+       if (hwerrs & INFINIPATH_HWE_IBCBUSFRSPCPARITYERR)
+               strlcat(msg, "[IPATH2IB Parity]", msgl);
+       if (hwerrs & INFINIPATH_HWE_HTCBUSIREQPARITYERR)
+               strlcat(msg, "[HTC Ireq Parity]", msgl);
+       if (hwerrs & INFINIPATH_HWE_HTCBUSTREQPARITYERR)
+               strlcat(msg, "[HTC Treq Parity]", msgl);
+       if (hwerrs & INFINIPATH_HWE_HTCBUSTRESPPARITYERR)
+               strlcat(msg, "[HTC Tresp Parity]", msgl);
+
+       if (hwerrs & (_IPATH_HTLINK0_CRCBITS | _IPATH_HTLINK1_CRCBITS))
+               hwerr_crcbits(dd, hwerrs, msg, msgl);
+
+       if (hwerrs & INFINIPATH_HWE_HTCMISCERR5)
+               strlcat(msg, "[HT core Misc5]", msgl);
+       if (hwerrs & INFINIPATH_HWE_HTCMISCERR6)
+               strlcat(msg, "[HT core Misc6]", msgl);
+       if (hwerrs & INFINIPATH_HWE_HTCMISCERR7)
+               strlcat(msg, "[HT core Misc7]", msgl);
+       if (hwerrs & INFINIPATH_HWE_MEMBISTFAILED) {
+               strlcat(msg, "[Memory BIST test failed, HT-400 unusable]",
+                       msgl);
+               /* ignore from now on, so disable until driver reloaded */
+               dd->ipath_hwerrmask &= ~INFINIPATH_HWE_MEMBISTFAILED;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask,
+                                dd->ipath_hwerrmask);
+       }
+#define _IPATH_PLL_FAIL (INFINIPATH_HWE_COREPLL_FBSLIP |       \
+                        INFINIPATH_HWE_COREPLL_RFSLIP |        \
+                        INFINIPATH_HWE_HTBPLL_FBSLIP |         \
+                        INFINIPATH_HWE_HTBPLL_RFSLIP |         \
+                        INFINIPATH_HWE_HTAPLL_FBSLIP |         \
+                        INFINIPATH_HWE_HTAPLL_RFSLIP)
+
+       if (hwerrs & _IPATH_PLL_FAIL) {
+               snprintf(bitsmsg, sizeof bitsmsg,
+                        "[PLL failed (%llx), HT-400 unusable]",
+                        (unsigned long long) (hwerrs & _IPATH_PLL_FAIL));
+               strlcat(msg, bitsmsg, msgl);
+               /* ignore from now on, so disable until driver reloaded */
+               dd->ipath_hwerrmask &= ~(hwerrs & _IPATH_PLL_FAIL);
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask,
+                                dd->ipath_hwerrmask);
+       }
+
+       if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED) {
+               /*
+                * If it occurs, it is left masked since the eternal
+                * interface is unused
+                */
+               dd->ipath_hwerrmask &= ~INFINIPATH_HWE_SERDESPLLFAILED;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask,
+                                dd->ipath_hwerrmask);
+       }
+
+       if (hwerrs & INFINIPATH_HWE_RXDSYNCMEMPARITYERR)
+               strlcat(msg, "[Rx Dsync]", msgl);
+       if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED)
+               strlcat(msg, "[SerDes PLL]", msgl);
+
+       ipath_dev_err(dd, "%s hardware error\n", msg);
+       if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg)
+               /*
+                * for status file; if no trailing brace is copied,
+                * we'll know it was truncated.
+                */
+               snprintf(dd->ipath_freezemsg,
+                        dd->ipath_freezelen, "{%s}", msg);
+
+bail:;
+}
+
+/**
+ * ipath_ht_boardname - fill in the board name
+ * @dd: the infinipath device
+ * @name: the output buffer
+ * @namelen: the size of the output buffer
+ *
+ * fill in the board name, based on the board revision register
+ */
+static int ipath_ht_boardname(struct ipath_devdata *dd, char *name,
+                             size_t namelen)
+{
+       char *n = NULL;
+       u8 boardrev = dd->ipath_boardrev;
+       int ret;
+
+       switch (boardrev) {
+       case 4:         /* Ponderosa is one of the bringup boards */
+               n = "Ponderosa";
+               break;
+       case 5:         /* HT-460 original production board */
+               n = "InfiniPath_HT-460";
+               break;
+       case 6:
+               n = "OEM_Board_3";
+               break;
+       case 7:
+               /* HT-460 small form factor production board */
+               n = "InfiniPath_HT-465";
+               break;
+       case 8:
+               n = "LS/X-1";
+               break;
+       case 9:         /* Comstock bringup test board */
+               n = "Comstock";
+               break;
+       case 10:
+               n = "OEM_Board_2";
+               break;
+       case 11:
+               n = "InfiniPath_HT-470";
+               break;
+       case 12:
+               n = "OEM_Board_4";
+               break;
+       default:                /* don't know, just print the number */
+               ipath_dev_err(dd, "Don't yet know about board "
+                             "with ID %u\n", boardrev);
+               snprintf(name, namelen, "Unknown_InfiniPath_HT-4xx_%u",
+                        boardrev);
+               break;
+       }
+       if (n)
+               snprintf(name, namelen, "%s", n);
+
+       if (dd->ipath_majrev != 3 || dd->ipath_minrev != 2) {
+               /*
+                * This version of the driver only supports the HT-400
+                * Rev 3.2
+                */
+               ipath_dev_err(dd,
+                             "Unsupported HT-400 revision %u.%u!\n",
+                             dd->ipath_majrev, dd->ipath_minrev);
+               ret = 1;
+               goto bail;
+       }
+       /*
+        * pkt/word counters are 32 bit, and therefore wrap fast enough
+        * that we snapshot them from a timer, and maintain 64 bit shadow
+        * copies
+        */
+       dd->ipath_flags |= IPATH_32BITCOUNTERS;
+       if (dd->ipath_htspeed != 800)
+               ipath_dev_err(dd,
+                             "Incorrectly configured for HT @ %uMHz\n",
+                             dd->ipath_htspeed);
+       if (dd->ipath_boardrev == 7 || dd->ipath_boardrev == 11 ||
+           dd->ipath_boardrev == 6)
+               dd->ipath_flags |= IPATH_GPIO_INTR;
+       else
+               dd->ipath_flags |= IPATH_POLL_RX_INTR;
+       if (dd->ipath_boardrev == 8) {  /* LS/X-1 */
+               u64 val;
+               val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus);
+               if (val & INFINIPATH_EXTS_SERDESSEL) {
+                       /*
+                        * hardware disabled
+                        *
+                        * This means that the chip is hardware disabled,
+                        * and will not be able to bring up the link,
+                        * in any case.  We special case this and abort
+                        * early, to avoid later messages.  We also set
+                        * the DISABLED status bit
+                        */
+                       ipath_dbg("Unit %u is hardware-disabled\n",
+                                 dd->ipath_unit);
+                       *dd->ipath_statusp |= IPATH_STATUS_DISABLED;
+                       /* this value is handled differently */
+                       ret = 2;
+                       goto bail;
+               }
+       }
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+static void ipath_check_htlink(struct ipath_devdata *dd)
+{
+       u8 linkerr, link_off, i;
+
+       for (i = 0; i < 2; i++) {
+               link_off = dd->ipath_ht_slave_off + i * 4 + 0xd;
+               if (pci_read_config_byte(dd->pcidev, link_off, &linkerr))
+                       dev_info(&dd->pcidev->dev, "Couldn't read "
+                                "linkerror%d of HT slave/primary block\n",
+                                i);
+               else if (linkerr & 0xf0) {
+                       ipath_cdbg(VERBOSE, "HT linkerr%d bits 0x%x set, "
+                                  "clearing\n", linkerr >> 4, i);
+                       /*
+                        * writing the linkerr bits that are set should
+                        * clear them
+                        */
+                       if (pci_write_config_byte(dd->pcidev, link_off,
+                                                 linkerr))
+                               ipath_dbg("Failed write to clear HT "
+                                         "linkerror%d\n", i);
+                       if (pci_read_config_byte(dd->pcidev, link_off,
+                                                &linkerr))
+                               dev_info(&dd->pcidev->dev,
+                                        "Couldn't reread linkerror%d of "
+                                        "HT slave/primary block\n", i);
+                       else if (linkerr & 0xf0)
+                               dev_info(&dd->pcidev->dev,
+                                        "HT linkerror%d bits 0x%x "
+                                        "couldn't be cleared\n",
+                                        i, linkerr >> 4);
+               }
+       }
+}
+
+static int ipath_setup_ht_reset(struct ipath_devdata *dd)
+{
+       ipath_dbg("No reset possible for HT-400\n");
+       return 0;
+}
+
+#define HT_CAPABILITY_ID   0x08        /* HT capabilities not defined in kernel */
+#define HT_INTR_DISC_CONFIG  0x80      /* HT interrupt and discovery cap */
+#define HT_INTR_REG_INDEX    2 /* intconfig requires indirect accesses */
+
+/*
+ * Bits 13-15 of command==0 is slave/primary block.  Clear any HT CRC
+ * errors.  We only bother to do this at load time, because it's OK if
+ * it happened before we were loaded (first time after boot/reset),
+ * but any time after that, it's fatal anyway.  Also need to not check
+ * for for upper byte errors if we are in 8 bit mode, so figure out
+ * our width.  For now, at least, also complain if it's 8 bit.
+ */
+static void slave_or_pri_blk(struct ipath_devdata *dd, struct pci_dev *pdev,
+                            int pos, u8 cap_type)
+{
+       u8 linkwidth = 0, linkerr, link_a_b_off, link_off;
+       u16 linkctrl = 0;
+       int i;
+
+       dd->ipath_ht_slave_off = pos;
+       /* command word, master_host bit */
+       /* master host || slave */
+       if ((cap_type >> 2) & 1)
+               link_a_b_off = 4;
+       else
+               link_a_b_off = 0;
+       ipath_cdbg(VERBOSE, "HT%u (Link %c) connected to processor\n",
+                  link_a_b_off ? 1 : 0,
+                  link_a_b_off ? 'B' : 'A');
+
+       link_a_b_off += pos;
+
+       /*
+        * check both link control registers; clear both HT CRC sets if
+        * necessary.
+        */
+       for (i = 0; i < 2; i++) {
+               link_off = pos + i * 4 + 0x4;
+               if (pci_read_config_word(pdev, link_off, &linkctrl))
+                       ipath_dev_err(dd, "Couldn't read HT link control%d "
+                                     "register\n", i);
+               else if (linkctrl & (0xf << 8)) {
+                       ipath_cdbg(VERBOSE, "Clear linkctrl%d CRC Error "
+                                  "bits %x\n", i, linkctrl & (0xf << 8));
+                       /*
+                        * now write them back to clear the error.
+                        */
+                       pci_write_config_byte(pdev, link_off,
+                                             linkctrl & (0xf << 8));
+               }
+       }
+
+       /*
+        * As with HT CRC bits, same for protocol errors that might occur
+        * during boot.
+        */
+       for (i = 0; i < 2; i++) {
+               link_off = pos + i * 4 + 0xd;
+               if (pci_read_config_byte(pdev, link_off, &linkerr))
+                       dev_info(&pdev->dev, "Couldn't read linkerror%d "
+                                "of HT slave/primary block\n", i);
+               else if (linkerr & 0xf0) {
+                       ipath_cdbg(VERBOSE, "HT linkerr%d bits 0x%x set, "
+                                  "clearing\n", linkerr >> 4, i);
+                       /*
+                        * writing the linkerr bits that are set will clear
+                        * them
+                        */
+                       if (pci_write_config_byte
+                           (pdev, link_off, linkerr))
+                               ipath_dbg("Failed write to clear HT "
+                                         "linkerror%d\n", i);
+                       if (pci_read_config_byte(pdev, link_off, &linkerr))
+                               dev_info(&pdev->dev, "Couldn't reread "
+                                        "linkerror%d of HT slave/primary "
+                                        "block\n", i);
+                       else if (linkerr & 0xf0)
+                               dev_info(&pdev->dev, "HT linkerror%d bits "
+                                        "0x%x couldn't be cleared\n",
+                                        i, linkerr >> 4);
+               }
+       }
+
+       /*
+        * this is just for our link to the host, not devices connected
+        * through tunnel.
+        */
+
+       if (pci_read_config_byte(pdev, link_a_b_off + 7, &linkwidth))
+               ipath_dev_err(dd, "Couldn't read HT link width "
+                             "config register\n");
+       else {
+               u32 width;
+               switch (linkwidth & 7) {
+               case 5:
+                       width = 4;
+                       break;
+               case 4:
+                       width = 2;
+                       break;
+               case 3:
+                       width = 32;
+                       break;
+               case 1:
+                       width = 16;
+                       break;
+               case 0:
+               default:        /* if wrong, assume 8 bit */
+                       width = 8;
+                       break;
+               }
+
+               dd->ipath_htwidth = width;
+
+               if (linkwidth != 0x11) {
+                       ipath_dev_err(dd, "Not configured for 16 bit HT "
+                                     "(%x)\n", linkwidth);
+                       if (!(linkwidth & 0xf)) {
+                               ipath_dbg("Will ignore HT lane1 errors\n");
+                               dd->ipath_flags |= IPATH_8BIT_IN_HT0;
+                       }
+               }
+       }
+
+       /*
+        * this is just for our link to the host, not devices connected
+        * through tunnel.
+        */
+       if (pci_read_config_byte(pdev, link_a_b_off + 0xd, &linkwidth))
+               ipath_dev_err(dd, "Couldn't read HT link frequency "
+                             "config register\n");
+       else {
+               u32 speed;
+               switch (linkwidth & 0xf) {
+               case 6:
+                       speed = 1000;
+                       break;
+               case 5:
+                       speed = 800;
+                       break;
+               case 4:
+                       speed = 600;
+                       break;
+               case 3:
+                       speed = 500;
+                       break;
+               case 2:
+                       speed = 400;
+                       break;
+               case 1:
+                       speed = 300;
+                       break;
+               default:
+                       /*
+                        * assume reserved and vendor-specific are 200...
+                        */
+               case 0:
+                       speed = 200;
+                       break;
+               }
+               dd->ipath_htspeed = speed;
+       }
+}
+
+static int set_int_handler(struct ipath_devdata *dd, struct pci_dev *pdev,
+                           int pos)
+{
+       u32 int_handler_addr_lower;
+       u32 int_handler_addr_upper;
+       u64 ihandler;
+       u32 intvec;
+
+       /* use indirection register to get the intr handler */
+       pci_write_config_byte(pdev, pos + HT_INTR_REG_INDEX, 0x10);
+       pci_read_config_dword(pdev, pos + 4, &int_handler_addr_lower);
+       pci_write_config_byte(pdev, pos + HT_INTR_REG_INDEX, 0x11);
+       pci_read_config_dword(pdev, pos + 4, &int_handler_addr_upper);
+
+       ihandler = (u64) int_handler_addr_lower |
+               ((u64) int_handler_addr_upper << 32);
+
+       /*
+        * kernels with CONFIG_PCI_MSI set the vector in the irq field of
+        * struct pci_device, so we use that to program the HT-400 internal
+        * interrupt register (not config space) with that value. The BIOS
+        * must still have done the basic MSI setup.
+        */
+       intvec = pdev->irq;
+       /*
+        * clear any vector bits there; normally not set but we'll overload
+        * this for some debug purposes (setting the HTC debug register
+        * value from software, rather than GPIOs), so it might be set on a
+        * driver reload.
+        */
+       ihandler &= ~0xff0000;
+       /* x86 vector goes in intrinfo[23:16] */
+       ihandler |= intvec << 16;
+       ipath_cdbg(VERBOSE, "ihandler lower %x, upper %x, intvec %x, "
+                  "interruptconfig %llx\n", int_handler_addr_lower,
+                  int_handler_addr_upper, intvec,
+                  (unsigned long long) ihandler);
+
+       /* can't program yet, so save for interrupt setup */
+       dd->ipath_intconfig = ihandler;
+       /* keep going, so we find link control stuff also */
+
+       return ihandler != 0;
+}
+
+/**
+ * ipath_setup_ht_config - setup the interruptconfig register
+ * @dd: the infinipath device
+ * @pdev: the PCI device
+ *
+ * setup the interruptconfig register from the HT config info.
+ * Also clear CRC errors in HT linkcontrol, if necessary.
+ * This is done only for the real hardware.  It is done before
+ * chip address space is initted, so can't touch infinipath registers
+ */
+static int ipath_setup_ht_config(struct ipath_devdata *dd,
+                                struct pci_dev *pdev)
+{
+       int pos, ret = 0;
+       int ihandler = 0;
+
+       /*
+        * Read the capability info to find the interrupt info, and also
+        * handle clearing CRC errors in linkctrl register if necessary.  We
+        * do this early, before we ever enable errors or hardware errors,
+        * mostly to avoid causing the chip to enter freeze mode.
+        */
+       pos = pci_find_capability(pdev, HT_CAPABILITY_ID);
+       if (!pos) {
+               ipath_dev_err(dd, "Couldn't find HyperTransport "
+                             "capability; no interrupts\n");
+               ret = -ENODEV;
+               goto bail;
+       }
+       do {
+               u8 cap_type;
+
+               /* the HT capability type byte is 3 bytes after the
+                * capability byte.
+                */
+               if (pci_read_config_byte(pdev, pos + 3, &cap_type)) {
+                       dev_info(&pdev->dev, "Couldn't read config "
+                                "command @ %d\n", pos);
+                       continue;
+               }
+               if (!(cap_type & 0xE0))
+                       slave_or_pri_blk(dd, pdev, pos, cap_type);
+               else if (cap_type == HT_INTR_DISC_CONFIG)
+                       ihandler = set_int_handler(dd, pdev, pos);
+       } while ((pos = pci_find_next_capability(pdev, pos,
+                                                HT_CAPABILITY_ID)));
+
+       if (!ihandler) {
+               ipath_dev_err(dd, "Couldn't find interrupt handler in "
+                             "config space\n");
+               ret = -ENODEV;
+       }
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_setup_ht_cleanup - clean up any per-chip chip-specific stuff
+ * @dd: the infinipath device
+ *
+ * Called during driver unload.
+ * This is currently a nop for the HT-400, not for all chips
+ */
+static void ipath_setup_ht_cleanup(struct ipath_devdata *dd)
+{
+}
+
+/**
+ * ipath_setup_ht_setextled - set the state of the two external LEDs
+ * @dd: the infinipath device
+ * @lst: the L state
+ * @ltst: the LT state
+ *
+ * Set the state of the two external LEDs, to indicate physical and
+ * logical state of IB link.   For this chip (at least with recommended
+ * board pinouts), LED1 is Green (physical state), and LED2 is Yellow
+ * (logical state)
+ *
+ * Note:  We try to match the Mellanox HCA LED behavior as best
+ * we can.  Green indicates physical link state is OK (something is
+ * plugged in, and we can train).
+ * Amber indicates the link is logically up (ACTIVE).
+ * Mellanox further blinks the amber LED to indicate data packet
+ * activity, but we have no hardware support for that, so it would
+ * require waking up every 10-20 msecs and checking the counters
+ * on the chip, and then turning the LED off if appropriate.  That's
+ * visible overhead, so not something we will do.
+ *
+ */
+static void ipath_setup_ht_setextled(struct ipath_devdata *dd,
+                                    u64 lst, u64 ltst)
+{
+       u64 extctl;
+
+       /* the diags use the LED to indicate diag info, so we leave
+        * the external LED alone when the diags are running */
+       if (ipath_diag_inuse)
+               return;
+
+       /*
+        * start by setting both LED control bits to off, then turn
+        * on the appropriate bit(s).
+        */
+       if (dd->ipath_boardrev == 8) { /* LS/X-1 uses different pins */
+               /*
+                * major difference is that INFINIPATH_EXTC_LEDGBLERR_OFF
+                * is inverted,  because it is normally used to indicate
+                * a hardware fault at reset, if there were errors
+                */
+               extctl = (dd->ipath_extctrl & ~INFINIPATH_EXTC_LEDGBLOK_ON)
+                       | INFINIPATH_EXTC_LEDGBLERR_OFF;
+               if (ltst == INFINIPATH_IBCS_LT_STATE_LINKUP)
+                       extctl &= ~INFINIPATH_EXTC_LEDGBLERR_OFF;
+               if (lst == INFINIPATH_IBCS_L_STATE_ACTIVE)
+                       extctl |= INFINIPATH_EXTC_LEDGBLOK_ON;
+       }
+       else {
+               extctl = dd->ipath_extctrl &
+                       ~(INFINIPATH_EXTC_LED1PRIPORT_ON |
+                         INFINIPATH_EXTC_LED2PRIPORT_ON);
+               if (ltst == INFINIPATH_IBCS_LT_STATE_LINKUP)
+                       extctl |= INFINIPATH_EXTC_LED1PRIPORT_ON;
+               if (lst == INFINIPATH_IBCS_L_STATE_ACTIVE)
+                       extctl |= INFINIPATH_EXTC_LED2PRIPORT_ON;
+       }
+       dd->ipath_extctrl = extctl;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl);
+}
+
+static void ipath_init_ht_variables(void)
+{
+       ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
+       ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
+       ipath_gpio_sda = IPATH_GPIO_SDA;
+       ipath_gpio_scl = IPATH_GPIO_SCL;
+
+       infinipath_i_bitsextant =
+               (INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) |
+               (INFINIPATH_I_RCVAVAIL_MASK <<
+                INFINIPATH_I_RCVAVAIL_SHIFT) |
+               INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT |
+               INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO;
+
+       infinipath_e_bitsextant =
+               INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC |
+               INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN |
+               INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN |
+               INFINIPATH_E_RSHORTPKTLEN | INFINIPATH_E_RUNEXPCHAR |
+               INFINIPATH_E_RUNSUPVL | INFINIPATH_E_REBP |
+               INFINIPATH_E_RIBFLOW | INFINIPATH_E_RBADVERSION |
+               INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL |
+               INFINIPATH_E_RBADTID | INFINIPATH_E_RHDRLEN |
+               INFINIPATH_E_RHDR | INFINIPATH_E_RIBLOSTLINK |
+               INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SMAXPKTLEN |
+               INFINIPATH_E_SUNDERRUN | INFINIPATH_E_SPKTLEN |
+               INFINIPATH_E_SDROPPEDSMPPKT | INFINIPATH_E_SDROPPEDDATAPKT |
+               INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SUNEXPERRPKTNUM |
+               INFINIPATH_E_SUNSUPVL | INFINIPATH_E_IBSTATUSCHANGED |
+               INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET |
+               INFINIPATH_E_HARDWARE;
+
+       infinipath_hwe_bitsextant =
+               (INFINIPATH_HWE_HTCMEMPARITYERR_MASK <<
+                INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT) |
+               (INFINIPATH_HWE_TXEMEMPARITYERR_MASK <<
+                INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) |
+               (INFINIPATH_HWE_RXEMEMPARITYERR_MASK <<
+                INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) |
+               INFINIPATH_HWE_HTCLNKABYTE0CRCERR |
+               INFINIPATH_HWE_HTCLNKABYTE1CRCERR |
+               INFINIPATH_HWE_HTCLNKBBYTE0CRCERR |
+               INFINIPATH_HWE_HTCLNKBBYTE1CRCERR |
+               INFINIPATH_HWE_HTCMISCERR4 |
+               INFINIPATH_HWE_HTCMISCERR5 | INFINIPATH_HWE_HTCMISCERR6 |
+               INFINIPATH_HWE_HTCMISCERR7 |
+               INFINIPATH_HWE_HTCBUSTREQPARITYERR |
+               INFINIPATH_HWE_HTCBUSTRESPPARITYERR |
+               INFINIPATH_HWE_HTCBUSIREQPARITYERR |
+               INFINIPATH_HWE_RXDSYNCMEMPARITYERR |
+               INFINIPATH_HWE_MEMBISTFAILED |
+               INFINIPATH_HWE_COREPLL_FBSLIP |
+               INFINIPATH_HWE_COREPLL_RFSLIP |
+               INFINIPATH_HWE_HTBPLL_FBSLIP |
+               INFINIPATH_HWE_HTBPLL_RFSLIP |
+               INFINIPATH_HWE_HTAPLL_FBSLIP |
+               INFINIPATH_HWE_HTAPLL_RFSLIP |
+               INFINIPATH_HWE_SERDESPLLFAILED |
+               INFINIPATH_HWE_IBCBUSTOSPCPARITYERR |
+               INFINIPATH_HWE_IBCBUSFRSPCPARITYERR;
+
+       infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
+       infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
+}
+
+/**
+ * ipath_ht_init_hwerrors - enable hardware errors
+ * @dd: the infinipath device
+ *
+ * now that we have finished initializing everything that might reasonably
+ * cause a hardware error, and cleared those errors bits as they occur,
+ * we can enable hardware errors in the mask (potentially enabling
+ * freeze mode), and enable hardware errors as errors (along with
+ * everything else) in errormask
+ */
+static void ipath_ht_init_hwerrors(struct ipath_devdata *dd)
+{
+       ipath_err_t val;
+       u64 extsval;
+
+       extsval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus);
+
+       if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST))
+               ipath_dev_err(dd, "MemBIST did not complete!\n");
+
+       ipath_check_htlink(dd);
+
+       /* barring bugs, all hwerrors become interrupts, which can */
+       val = -1LL;
+       /* don't look at crc lane1 if 8 bit */
+       if (dd->ipath_flags & IPATH_8BIT_IN_HT0)
+               val &= ~infinipath_hwe_htclnkabyte1crcerr;
+       /* don't look at crc lane1 if 8 bit */
+       if (dd->ipath_flags & IPATH_8BIT_IN_HT1)
+               val &= ~infinipath_hwe_htclnkbbyte1crcerr;
+
+       /*
+        * disable RXDSYNCMEMPARITY because external serdes is unused,
+        * and therefore the logic will never be used or initialized,
+        * and uninitialized state will normally result in this error
+        * being asserted.  Similarly for the external serdess pll
+        * lock signal.
+        */
+       val &= ~(INFINIPATH_HWE_SERDESPLLFAILED |
+                INFINIPATH_HWE_RXDSYNCMEMPARITYERR);
+
+       /*
+        * Disable MISCERR4 because of an inversion in the HT core
+        * logic checking for errors that cause this bit to be set.
+        * The errata can also cause the protocol error bit to be set
+        * in the HT config space linkerror register(s).
+        */
+       val &= ~INFINIPATH_HWE_HTCMISCERR4;
+
+       /*
+        * PLL ignored because MDIO interface has a logic problem
+        * for reads, on Comstock and Ponderosa.  BRINGUP
+        */
+       if (dd->ipath_boardrev == 4 || dd->ipath_boardrev == 9)
+               val &= ~INFINIPATH_HWE_SERDESPLLFAILED;
+       dd->ipath_hwerrmask = val;
+}
+
+/**
+ * ipath_ht_bringup_serdes - bring up the serdes
+ * @dd: the infinipath device
+ */
+static int ipath_ht_bringup_serdes(struct ipath_devdata *dd)
+{
+       u64 val, config1;
+       int ret = 0, change = 0;
+
+       ipath_dbg("Trying to bringup serdes\n");
+
+       if (ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwerrstatus) &
+           INFINIPATH_HWE_SERDESPLLFAILED)
+       {
+               ipath_dbg("At start, serdes PLL failed bit set in "
+                         "hwerrstatus, clearing and continuing\n");
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear,
+                                INFINIPATH_HWE_SERDESPLLFAILED);
+       }
+
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
+       config1 = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig1);
+
+       ipath_cdbg(VERBOSE, "Initial serdes status is config0=%llx "
+                  "config1=%llx, sstatus=%llx xgxs %llx\n",
+                  (unsigned long long) val, (unsigned long long) config1,
+                  (unsigned long long)
+                  ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesstatus),
+                  (unsigned long long)
+                  ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig));
+
+       /* force reset on */
+       val |= INFINIPATH_SERDC0_RESET_PLL
+               /* | INFINIPATH_SERDC0_RESET_MASK */
+               ;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val);
+       udelay(15);             /* need pll reset set at least for a bit */
+
+       if (val & INFINIPATH_SERDC0_RESET_PLL) {
+               u64 val2 = val &= ~INFINIPATH_SERDC0_RESET_PLL;
+               /* set lane resets, and tx idle, during pll reset */
+               val2 |= INFINIPATH_SERDC0_RESET_MASK |
+                       INFINIPATH_SERDC0_TXIDLE;
+               ipath_cdbg(VERBOSE, "Clearing serdes PLL reset (writing "
+                          "%llx)\n", (unsigned long long) val2);
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0,
+                                val2);
+               /*
+                * be sure chip saw it
+                */
+               val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+               /*
+                * need pll reset clear at least 11 usec before lane
+                * resets cleared; give it a few more
+                */
+               udelay(15);
+               val = val2;     /* for check below */
+       }
+
+       if (val & (INFINIPATH_SERDC0_RESET_PLL |
+                  INFINIPATH_SERDC0_RESET_MASK |
+                  INFINIPATH_SERDC0_TXIDLE)) {
+               val &= ~(INFINIPATH_SERDC0_RESET_PLL |
+                        INFINIPATH_SERDC0_RESET_MASK |
+                        INFINIPATH_SERDC0_TXIDLE);
+               /* clear them */
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0,
+                                val);
+       }
+
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig);
+       if (((val >> INFINIPATH_XGXS_MDIOADDR_SHIFT) &
+            INFINIPATH_XGXS_MDIOADDR_MASK) != 3) {
+               val &= ~(INFINIPATH_XGXS_MDIOADDR_MASK <<
+                        INFINIPATH_XGXS_MDIOADDR_SHIFT);
+               /*
+                * we use address 3
+                */
+               val |= 3ULL << INFINIPATH_XGXS_MDIOADDR_SHIFT;
+               change = 1;
+       }
+       if (val & INFINIPATH_XGXS_RESET) {
+               /* normally true after boot */
+               val &= ~INFINIPATH_XGXS_RESET;
+               change = 1;
+       }
+       if (change)
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, val);
+
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
+
+       /* clear current and de-emphasis bits */
+       config1 &= ~0x0ffffffff00ULL;
+       /* set current to 20ma */
+       config1 |= 0x00000000000ULL;
+       /* set de-emphasis to -5.68dB */
+       config1 |= 0x0cccc000000ULL;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig1, config1);
+
+       ipath_cdbg(VERBOSE, "After setup: serdes status is config0=%llx "
+                  "config1=%llx, sstatus=%llx xgxs %llx\n",
+                  (unsigned long long) val, (unsigned long long) config1,
+                  (unsigned long long)
+                  ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesstatus),
+                  (unsigned long long)
+                  ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig));
+
+       if (!ipath_waitfor_mdio_cmdready(dd)) {
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_mdio,
+                                ipath_mdio_req(IPATH_MDIO_CMD_READ, 31,
+                                               IPATH_MDIO_CTRL_XGXS_REG_8,
+                                               0));
+               if (ipath_waitfor_complete(dd, dd->ipath_kregs->kr_mdio,
+                                          IPATH_MDIO_DATAVALID, &val))
+                       ipath_dbg("Never got MDIO data for XGXS status "
+                                 "read\n");
+               else
+                       ipath_cdbg(VERBOSE, "MDIO Read reg8, "
+                                  "'bank' 31 %x\n", (u32) val);
+       } else
+               ipath_dbg("Never got MDIO cmdready for XGXS status read\n");
+
+       return ret;             /* for now, say we always succeeded */
+}
+
+/**
+ * ipath_ht_quiet_serdes - set serdes to txidle
+ * @dd: the infinipath device
+ * driver is being unloaded
+ */
+static void ipath_ht_quiet_serdes(struct ipath_devdata *dd)
+{
+       u64 val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
+
+       val |= INFINIPATH_SERDC0_TXIDLE;
+       ipath_dbg("Setting TxIdleEn on serdes (config0 = %llx)\n",
+                 (unsigned long long) val);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val);
+}
+
+static int ipath_ht_intconfig(struct ipath_devdata *dd)
+{
+       int ret;
+
+       if (!dd->ipath_intconfig) {
+               ipath_dev_err(dd, "No interrupts enabled, couldn't setup "
+                             "interrupt address\n");
+               ret = 1;
+               goto bail;
+       }
+
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_interruptconfig,
+                        dd->ipath_intconfig);  /* interrupt address */
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_pe_put_tid - write a TID in chip
+ * @dd: the infinipath device
+ * @tidptr: pointer to the expected TID (in chip) to udpate
+ * @tidtype: 0 for eager, 1 for expected
+ * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing
+ *
+ * This exists as a separate routine to allow for special locking etc.
+ * It's used for both the full cleanup on exit, as well as the normal
+ * setup and teardown.
+ */
+static void ipath_ht_put_tid(struct ipath_devdata *dd,
+                            u64 __iomem *tidptr, u32 type,
+                            unsigned long pa)
+{
+       if (pa != dd->ipath_tidinvalid) {
+               if (unlikely((pa & ~INFINIPATH_RT_ADDR_MASK))) {
+                       dev_info(&dd->pcidev->dev,
+                                "physaddr %lx has more than "
+                                "40 bits, using only 40!!!\n", pa);
+                       pa &= INFINIPATH_RT_ADDR_MASK;
+               }
+               if (type == 0)
+                       pa |= dd->ipath_tidtemplate;
+               else {
+                       /* in words (fixed, full page).  */
+                       u64 lenvalid = PAGE_SIZE >> 2;
+                       lenvalid <<= INFINIPATH_RT_BUFSIZE_SHIFT;
+                       pa |= lenvalid | INFINIPATH_RT_VALID;
+               }
+       }
+       if (dd->ipath_kregbase)
+               writeq(pa, tidptr);
+}
+
+/**
+ * ipath_ht_clear_tid - clear all TID entries for a port, expected and eager
+ * @dd: the infinipath device
+ * @port: the port
+ *
+ * Used from ipath_close(), and at chip initialization.
+ */
+static void ipath_ht_clear_tids(struct ipath_devdata *dd, unsigned port)
+{
+       u64 __iomem *tidbase;
+       int i;
+
+       if (!dd->ipath_kregbase)
+               return;
+
+       ipath_cdbg(VERBOSE, "Invalidate TIDs for port %u\n", port);
+
+       /*
+        * need to invalidate all of the expected TID entries for this
+        * port, so we don't have valid entries that might somehow get
+        * used (early in next use of this port, or through some bug)
+        */
+       tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) +
+                                  dd->ipath_rcvtidbase +
+                                  port * dd->ipath_rcvtidcnt *
+                                  sizeof(*tidbase));
+       for (i = 0; i < dd->ipath_rcvtidcnt; i++)
+               ipath_ht_put_tid(dd, &tidbase[i], 1, dd->ipath_tidinvalid);
+
+       tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) +
+                                  dd->ipath_rcvegrbase +
+                                  port * dd->ipath_rcvegrcnt *
+                                  sizeof(*tidbase));
+
+       for (i = 0; i < dd->ipath_rcvegrcnt; i++)
+               ipath_ht_put_tid(dd, &tidbase[i], 0, dd->ipath_tidinvalid);
+}
+
+/**
+ * ipath_ht_tidtemplate - setup constants for TID updates
+ * @dd: the infinipath device
+ *
+ * We setup stuff that we use a lot, to avoid calculating each time
+ */
+static void ipath_ht_tidtemplate(struct ipath_devdata *dd)
+{
+       dd->ipath_tidtemplate = dd->ipath_ibmaxlen >> 2;
+       dd->ipath_tidtemplate <<= INFINIPATH_RT_BUFSIZE_SHIFT;
+       dd->ipath_tidtemplate |= INFINIPATH_RT_VALID;
+
+       /*
+        * work around chip errata bug 7358, by marking invalid tids
+        * as having max length
+        */
+       dd->ipath_tidinvalid = (-1LL & INFINIPATH_RT_BUFSIZE_MASK) <<
+               INFINIPATH_RT_BUFSIZE_SHIFT;
+}
+
+static int ipath_ht_early_init(struct ipath_devdata *dd)
+{
+       u32 __iomem *piobuf;
+       u32 pioincr, val32, egrsize;
+       int i;
+
+       /*
+        * one cache line; long IB headers will spill over into received
+        * buffer
+        */
+       dd->ipath_rcvhdrentsize = 16;
+       dd->ipath_rcvhdrsize = IPATH_DFLT_RCVHDRSIZE;
+
+       /*
+        * For HT-400, we allocate a somewhat overly large eager buffer,
+        * such that we can guarantee that we can receive the largest
+        * packet that we can send out.  To truly support a 4KB MTU,
+        * we need to bump this to a large value.  To date, other than
+        * testing, we have never encountered an HCA that can really
+        * send 4KB MTU packets, so we do not handle that (we'll get
+        * errors interrupts if we ever see one).
+        */
+       dd->ipath_rcvegrbufsize = dd->ipath_piosize2k;
+       egrsize = dd->ipath_rcvegrbufsize;
+
+       /*
+        * the min() check here is currently a nop, but it may not
+        * always be, depending on just how we do ipath_rcvegrbufsize
+        */
+       dd->ipath_ibmaxlen = min(dd->ipath_piosize2k,
+                                dd->ipath_rcvegrbufsize);
+       dd->ipath_init_ibmaxlen = dd->ipath_ibmaxlen;
+       ipath_ht_tidtemplate(dd);
+
+       /*
+        * zero all the TID entries at startup.  We do this for sanity,
+        * in case of a previous driver crash of some kind, and also
+        * because the chip powers up with these memories in an unknown
+        * state.  Use portcnt, not cfgports, since this is for the
+        * full chip, not for current (possibly different) configuration
+        * value.
+        * Chip Errata bug 6447
+        */
+       for (val32 = 0; val32 < dd->ipath_portcnt; val32++)
+               ipath_ht_clear_tids(dd, val32);
+
+       /*
+        * write the pbc of each buffer, to be sure it's initialized, then
+        * cancel all the buffers, and also abort any packets that might
+        * have been in flight for some reason (the latter is for driver
+        * unload/reload, but isn't a bad idea at first init).  PIO send
+        * isn't enabled at this point, so there is no danger of sending
+        * these out on the wire.
+        * Chip Errata bug 6610
+        */
+       piobuf = (u32 __iomem *) (((char __iomem *)(dd->ipath_kregbase)) +
+                                 dd->ipath_piobufbase);
+       pioincr = dd->ipath_palign / sizeof(*piobuf);
+       for (i = 0; i < dd->ipath_piobcnt2k; i++) {
+               /*
+                * reasonable word count, just to init pbc
+                */
+               writel(16, piobuf);
+               piobuf += pioincr;
+       }
+       /*
+        * self-clearing
+        */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                        INFINIPATH_S_ABORT);
+       return 0;
+}
+
+/**
+ * ipath_init_ht_get_base_info - set chip-specific flags for user code
+ * @dd: the infinipath device
+ * @kbase: ipath_base_info pointer
+ *
+ * We set the PCIE flag because the lower bandwidth on PCIe vs
+ * HyperTransport can affect some user packet algorithims.
+ */
+static int ipath_ht_get_base_info(struct ipath_portdata *pd, void *kbase)
+{
+       struct ipath_base_info *kinfo = kbase;
+
+       kinfo->spi_runtime_flags |= IPATH_RUNTIME_HT |
+               IPATH_RUNTIME_RCVHDR_COPY;
+
+       return 0;
+}
+
+/**
+ * ipath_init_ht400_funcs - set up the chip-specific function pointers
+ * @dd: the infinipath device
+ *
+ * This is global, and is called directly at init to set up the
+ * chip-specific function pointers for later use.
+ */
+void ipath_init_ht400_funcs(struct ipath_devdata *dd)
+{
+       dd->ipath_f_intrsetup = ipath_ht_intconfig;
+       dd->ipath_f_bus = ipath_setup_ht_config;
+       dd->ipath_f_reset = ipath_setup_ht_reset;
+       dd->ipath_f_get_boardname = ipath_ht_boardname;
+       dd->ipath_f_init_hwerrors = ipath_ht_init_hwerrors;
+       dd->ipath_f_init_hwerrors = ipath_ht_init_hwerrors;
+       dd->ipath_f_early_init = ipath_ht_early_init;
+       dd->ipath_f_handle_hwerrors = ipath_ht_handle_hwerrors;
+       dd->ipath_f_quiet_serdes = ipath_ht_quiet_serdes;
+       dd->ipath_f_bringup_serdes = ipath_ht_bringup_serdes;
+       dd->ipath_f_clear_tids = ipath_ht_clear_tids;
+       dd->ipath_f_put_tid = ipath_ht_put_tid;
+       dd->ipath_f_cleanup = ipath_setup_ht_cleanup;
+       dd->ipath_f_setextled = ipath_setup_ht_setextled;
+       dd->ipath_f_get_base_info = ipath_ht_get_base_info;
+
+       /*
+        * initialize chip-specific variables
+        */
+       dd->ipath_f_tidtemplate = ipath_ht_tidtemplate;
+
+       /*
+        * setup the register offsets, since they are different for each
+        * chip
+        */
+       dd->ipath_kregs = &ipath_ht_kregs;
+       dd->ipath_cregs = &ipath_ht_cregs;
+
+       /*
+        * do very early init that is needed before ipath_f_bus is
+        * called
+        */
+       ipath_init_ht_variables();
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
new file mode 100644 (file)
index 0000000..2823ff9
--- /dev/null
@@ -0,0 +1,951 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
+
+#include "ipath_kernel.h"
+#include "ips_common.h"
+
+/*
+ * min buffers we want to have per port, after driver
+ */
+#define IPATH_MIN_USER_PORT_BUFCNT 8
+
+/*
+ * Number of ports we are configured to use (to allow for more pio
+ * buffers per port, etc.)  Zero means use chip value.
+ */
+static ushort ipath_cfgports;
+
+module_param_named(cfgports, ipath_cfgports, ushort, S_IRUGO);
+MODULE_PARM_DESC(cfgports, "Set max number of ports to use");
+
+/*
+ * Number of buffers reserved for driver (layered drivers and SMA
+ * send).  Reserved at end of buffer list.
+ */
+static ushort ipath_kpiobufs = 32;
+
+static int ipath_set_kpiobufs(const char *val, struct kernel_param *kp);
+
+module_param_call(kpiobufs, ipath_set_kpiobufs, param_get_uint,
+                 &ipath_kpiobufs, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(kpiobufs, "Set number of PIO buffers for driver");
+
+/**
+ * create_port0_egr - allocate the eager TID buffers
+ * @dd: the infinipath device
+ *
+ * This code is now quite different for user and kernel, because
+ * the kernel uses skb's, for the accelerated network performance.
+ * This is the kernel (port0) version.
+ *
+ * Allocate the eager TID buffers and program them into infinipath.
+ * We use the network layer alloc_skb() allocator to allocate the
+ * memory, and either use the buffers as is for things like SMA
+ * packets, or pass the buffers up to the ipath layered driver and
+ * thence the network layer, replacing them as we do so (see
+ * ipath_rcv_layer()).
+ */
+static int create_port0_egr(struct ipath_devdata *dd)
+{
+       unsigned e, egrcnt;
+       struct sk_buff **skbs;
+       int ret;
+
+       egrcnt = dd->ipath_rcvegrcnt;
+
+       skbs = vmalloc(sizeof(*dd->ipath_port0_skbs) * egrcnt);
+       if (skbs == NULL) {
+               ipath_dev_err(dd, "allocation error for eager TID "
+                             "skb array\n");
+               ret = -ENOMEM;
+               goto bail;
+       }
+       for (e = 0; e < egrcnt; e++) {
+               /*
+                * This is a bit tricky in that we allocate extra
+                * space for 2 bytes of the 14 byte ethernet header.
+                * These two bytes are passed in the ipath header so
+                * the rest of the data is word aligned.  We allocate
+                * 4 bytes so that the data buffer stays word aligned.
+                * See ipath_kreceive() for more details.
+                */
+               skbs[e] = ipath_alloc_skb(dd, GFP_KERNEL);
+               if (!skbs[e]) {
+                       ipath_dev_err(dd, "SKB allocation error for "
+                                     "eager TID %u\n", e);
+                       while (e != 0)
+                               dev_kfree_skb(skbs[--e]);
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+       }
+       /*
+        * After loop above, so we can test non-NULL to see if ready
+        * to use at receive, etc.
+        */
+       dd->ipath_port0_skbs = skbs;
+
+       for (e = 0; e < egrcnt; e++) {
+               unsigned long phys =
+                       virt_to_phys(dd->ipath_port0_skbs[e]->data);
+               dd->ipath_f_put_tid(dd, e + (u64 __iomem *)
+                                   ((char __iomem *) dd->ipath_kregbase +
+                                    dd->ipath_rcvegrbase), 0, phys);
+       }
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+static int bringup_link(struct ipath_devdata *dd)
+{
+       u64 val, ibc;
+       int ret = 0;
+
+       /* hold IBC in reset */
+       dd->ipath_control &= ~INFINIPATH_C_LINKENABLE;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
+                        dd->ipath_control);
+
+       /*
+        * Note that prior to try 14 or 15 of IB, the credit scaling
+        * wasn't working, because it was swapped for writes with the
+        * 1 bit default linkstate field
+        */
+
+       /* ignore pbc and align word */
+       val = dd->ipath_piosize2k - 2 * sizeof(u32);
+       /*
+        * for ICRC, which we only send in diag test pkt mode, and we
+        * don't need to worry about that for mtu
+        */
+       val += 1;
+       /*
+        * Set the IBC maxpktlength to the size of our pio buffers the
+        * maxpktlength is in words.  This is *not* the IB data MTU.
+        */
+       ibc = (val / sizeof(u32)) << INFINIPATH_IBCC_MAXPKTLEN_SHIFT;
+       /* in KB */
+       ibc |= 0x5ULL << INFINIPATH_IBCC_FLOWCTRLWATERMARK_SHIFT;
+       /*
+        * How often flowctrl sent.  More or less in usecs; balance against
+        * watermark value, so that in theory senders always get a flow
+        * control update in time to not let the IB link go idle.
+        */
+       ibc |= 0x3ULL << INFINIPATH_IBCC_FLOWCTRLPERIOD_SHIFT;
+       /* max error tolerance */
+       ibc |= 0xfULL << INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT;
+       /* use "real" buffer space for */
+       ibc |= 4ULL << INFINIPATH_IBCC_CREDITSCALE_SHIFT;
+       /* IB credit flow control. */
+       ibc |= 0xfULL << INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT;
+       /* initially come up waiting for TS1, without sending anything. */
+       dd->ipath_ibcctrl = ibc;
+       /*
+        * Want to start out with both LINKCMD and LINKINITCMD in NOP
+        * (0 and 0).  Don't put linkinitcmd in ipath_ibcctrl, want that
+        * to stay a NOP
+        */
+       ibc |= INFINIPATH_IBCC_LINKINITCMD_DISABLE <<
+               INFINIPATH_IBCC_LINKINITCMD_SHIFT;
+       ipath_cdbg(VERBOSE, "Writing 0x%llx to ibcctrl\n",
+                  (unsigned long long) ibc);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, ibc);
+
+       // be sure chip saw it
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+
+       ret = dd->ipath_f_bringup_serdes(dd);
+
+       if (ret)
+               dev_info(&dd->pcidev->dev, "Could not initialize SerDes, "
+                        "not usable\n");
+       else {
+               /* enable IBC */
+               dd->ipath_control |= INFINIPATH_C_LINKENABLE;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
+                                dd->ipath_control);
+       }
+
+       return ret;
+}
+
+static int init_chip_first(struct ipath_devdata *dd,
+                          struct ipath_portdata **pdp)
+{
+       struct ipath_portdata *pd = NULL;
+       int ret = 0;
+       u64 val;
+
+       /*
+        * skip cfgports stuff because we are not allocating memory,
+        * and we don't want problems if the portcnt changed due to
+        * cfgports.  We do still check and report a difference, if
+        * not same (should be impossible).
+        */
+       dd->ipath_portcnt =
+               ipath_read_kreg32(dd, dd->ipath_kregs->kr_portcnt);
+       if (!ipath_cfgports)
+               dd->ipath_cfgports = dd->ipath_portcnt;
+       else if (ipath_cfgports <= dd->ipath_portcnt) {
+               dd->ipath_cfgports = ipath_cfgports;
+               ipath_dbg("Configured to use %u ports out of %u in chip\n",
+                         dd->ipath_cfgports, dd->ipath_portcnt);
+       } else {
+               dd->ipath_cfgports = dd->ipath_portcnt;
+               ipath_dbg("Tried to configured to use %u ports; chip "
+                         "only supports %u\n", ipath_cfgports,
+                         dd->ipath_portcnt);
+       }
+       dd->ipath_pd = kzalloc(sizeof(*dd->ipath_pd) * dd->ipath_cfgports,
+                              GFP_KERNEL);
+
+       if (!dd->ipath_pd) {
+               ipath_dev_err(dd, "Unable to allocate portdata array, "
+                             "failing\n");
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       dd->ipath_lastegrheads = kzalloc(sizeof(*dd->ipath_lastegrheads)
+                                        * dd->ipath_cfgports,
+                                        GFP_KERNEL);
+       dd->ipath_lastrcvhdrqtails =
+               kzalloc(sizeof(*dd->ipath_lastrcvhdrqtails)
+                       * dd->ipath_cfgports, GFP_KERNEL);
+
+       if (!dd->ipath_lastegrheads || !dd->ipath_lastrcvhdrqtails) {
+               ipath_dev_err(dd, "Unable to allocate head arrays, "
+                             "failing\n");
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       dd->ipath_pd[0] = kzalloc(sizeof(*pd), GFP_KERNEL);
+
+       if (!dd->ipath_pd[0]) {
+               ipath_dev_err(dd, "Unable to allocate portdata for port "
+                             "0, failing\n");
+               ret = -ENOMEM;
+               goto done;
+       }
+       pd = dd->ipath_pd[0];
+       pd->port_dd = dd;
+       pd->port_port = 0;
+       pd->port_cnt = 1;
+       /* The port 0 pkey table is used by the layer interface. */
+       pd->port_pkeys[0] = IPS_DEFAULT_P_KEY;
+       dd->ipath_rcvtidcnt =
+               ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt);
+       dd->ipath_rcvtidbase =
+               ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidbase);
+       dd->ipath_rcvegrcnt =
+               ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvegrcnt);
+       dd->ipath_rcvegrbase =
+               ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvegrbase);
+       dd->ipath_palign =
+               ipath_read_kreg32(dd, dd->ipath_kregs->kr_pagealign);
+       dd->ipath_piobufbase =
+               ipath_read_kreg64(dd, dd->ipath_kregs->kr_sendpiobufbase);
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_sendpiosize);
+       dd->ipath_piosize2k = val & ~0U;
+       dd->ipath_piosize4k = val >> 32;
+       dd->ipath_ibmtu = 4096; /* default to largest legal MTU */
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_sendpiobufcnt);
+       dd->ipath_piobcnt2k = val & ~0U;
+       dd->ipath_piobcnt4k = val >> 32;
+       dd->ipath_pio2kbase =
+               (u32 __iomem *) (((char __iomem *) dd->ipath_kregbase) +
+                                (dd->ipath_piobufbase & 0xffffffff));
+       if (dd->ipath_piobcnt4k) {
+               dd->ipath_pio4kbase = (u32 __iomem *)
+                       (((char __iomem *) dd->ipath_kregbase) +
+                        (dd->ipath_piobufbase >> 32));
+               /*
+                * 4K buffers take 2 pages; we use roundup just to be
+                * paranoid; we calculate it once here, rather than on
+                * ever buf allocate
+                */
+               dd->ipath_4kalign = ALIGN(dd->ipath_piosize4k,
+                                         dd->ipath_palign);
+               ipath_dbg("%u 2k(%x) piobufs @ %p, %u 4k(%x) @ %p "
+                         "(%x aligned)\n",
+                         dd->ipath_piobcnt2k, dd->ipath_piosize2k,
+                         dd->ipath_pio2kbase, dd->ipath_piobcnt4k,
+                         dd->ipath_piosize4k, dd->ipath_pio4kbase,
+                         dd->ipath_4kalign);
+       }
+       else ipath_dbg("%u 2k piobufs @ %p\n",
+                      dd->ipath_piobcnt2k, dd->ipath_pio2kbase);
+
+       spin_lock_init(&dd->ipath_tid_lock);
+
+done:
+       *pdp = pd;
+       return ret;
+}
+
+/**
+ * init_chip_reset - re-initialize after a reset, or enable
+ * @dd: the infinipath device
+ * @pdp: output for port data
+ *
+ * sanity check at least some of the values after reset, and
+ * ensure no receive or transmit (explictly, in case reset
+ * failed
+ */
+static int init_chip_reset(struct ipath_devdata *dd,
+                          struct ipath_portdata **pdp)
+{
+       struct ipath_portdata *pd;
+       u32 rtmp;
+
+       *pdp = pd = dd->ipath_pd[0];
+       /* ensure chip does no sends or receives while we re-initialize */
+       dd->ipath_control = dd->ipath_sendctrl = dd->ipath_rcvctrl = 0U;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, 0);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, 0);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_control, 0);
+
+       rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_portcnt);
+       if (dd->ipath_portcnt != rtmp)
+               dev_info(&dd->pcidev->dev, "portcnt was %u before "
+                        "reset, now %u, using original\n",
+                        dd->ipath_portcnt, rtmp);
+       rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt);
+       if (rtmp != dd->ipath_rcvtidcnt)
+               dev_info(&dd->pcidev->dev, "tidcnt was %u before "
+                        "reset, now %u, using original\n",
+                        dd->ipath_rcvtidcnt, rtmp);
+       rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidbase);
+       if (rtmp != dd->ipath_rcvtidbase)
+               dev_info(&dd->pcidev->dev, "tidbase was %u before "
+                        "reset, now %u, using original\n",
+                        dd->ipath_rcvtidbase, rtmp);
+       rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvegrcnt);
+       if (rtmp != dd->ipath_rcvegrcnt)
+               dev_info(&dd->pcidev->dev, "egrcnt was %u before "
+                        "reset, now %u, using original\n",
+                        dd->ipath_rcvegrcnt, rtmp);
+       rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvegrbase);
+       if (rtmp != dd->ipath_rcvegrbase)
+               dev_info(&dd->pcidev->dev, "egrbase was %u before "
+                        "reset, now %u, using original\n",
+                        dd->ipath_rcvegrbase, rtmp);
+
+       return 0;
+}
+
+static int init_pioavailregs(struct ipath_devdata *dd)
+{
+       int ret;
+
+       dd->ipath_pioavailregs_dma = dma_alloc_coherent(
+               &dd->pcidev->dev, PAGE_SIZE, &dd->ipath_pioavailregs_phys,
+               GFP_KERNEL);
+       if (!dd->ipath_pioavailregs_dma) {
+               ipath_dev_err(dd, "failed to allocate PIOavail reg area "
+                             "in memory\n");
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       /*
+        * we really want L2 cache aligned, but for current CPUs of
+        * interest, they are the same.
+        */
+       dd->ipath_statusp = (u64 *)
+               ((char *)dd->ipath_pioavailregs_dma +
+                ((2 * L1_CACHE_BYTES +
+                  dd->ipath_pioavregs * sizeof(u64)) & ~L1_CACHE_BYTES));
+       /* copy the current value now that it's really allocated */
+       *dd->ipath_statusp = dd->_ipath_status;
+       /*
+        * setup buffer to hold freeze msg, accessible to apps,
+        * following statusp
+        */
+       dd->ipath_freezemsg = (char *)&dd->ipath_statusp[1];
+       /* and its length */
+       dd->ipath_freezelen = L1_CACHE_BYTES - sizeof(dd->ipath_statusp[0]);
+
+       if (dd->ipath_unit * 64 > (IPATH_PORT0_RCVHDRTAIL_SIZE - 64)) {
+               ipath_dev_err(dd, "unit %u too large for port 0 "
+                             "rcvhdrtail buffer size\n", dd->ipath_unit);
+               ret = -ENODEV;
+       }
+       else
+               ret = 0;
+
+       /* so we can get current tail in ipath_kreceive(), per chip */
+       dd->ipath_hdrqtailptr = &ipath_port0_rcvhdrtail[
+               dd->ipath_unit * (64 / sizeof(*ipath_port0_rcvhdrtail))];
+done:
+       return ret;
+}
+
+/**
+ * init_shadow_tids - allocate the shadow TID array
+ * @dd: the infinipath device
+ *
+ * allocate the shadow TID array, so we can ipath_munlock previous
+ * entries.  It may make more sense to move the pageshadow to the
+ * port data structure, so we only allocate memory for ports actually
+ * in use, since we at 8k per port, now.
+ */
+static void init_shadow_tids(struct ipath_devdata *dd)
+{
+       dd->ipath_pageshadow = (struct page **)
+               vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt *
+                       sizeof(struct page *));
+       if (!dd->ipath_pageshadow)
+               ipath_dev_err(dd, "failed to allocate shadow page * "
+                             "array, no expected sends!\n");
+       else
+               memset(dd->ipath_pageshadow, 0,
+                      dd->ipath_cfgports * dd->ipath_rcvtidcnt *
+                      sizeof(struct page *));
+}
+
+static void enable_chip(struct ipath_devdata *dd,
+                       struct ipath_portdata *pd, int reinit)
+{
+       u32 val;
+       int i;
+
+       if (!reinit) {
+               init_waitqueue_head(&ipath_sma_state_wait);
+       }
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+                        dd->ipath_rcvctrl);
+
+       /* Enable PIO send, and update of PIOavail regs to memory. */
+       dd->ipath_sendctrl = INFINIPATH_S_PIOENABLE |
+               INFINIPATH_S_PIOBUFAVAILUPD;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                        dd->ipath_sendctrl);
+
+       /*
+        * enable port 0 receive, and receive interrupt.  other ports
+        * done as user opens and inits them.
+        */
+       dd->ipath_rcvctrl = INFINIPATH_R_TAILUPD |
+               (1ULL << INFINIPATH_R_PORTENABLE_SHIFT) |
+               (1ULL << INFINIPATH_R_INTRAVAIL_SHIFT);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+                        dd->ipath_rcvctrl);
+
+       /*
+        * now ready for use.  this should be cleared whenever we
+        * detect a reset, or initiate one.
+        */
+       dd->ipath_flags |= IPATH_INITTED;
+
+       /*
+        * init our shadow copies of head from tail values, and write
+        * head values to match.
+        */
+       val = ipath_read_ureg32(dd, ur_rcvegrindextail, 0);
+       (void)ipath_write_ureg(dd, ur_rcvegrindexhead, val, 0);
+       dd->ipath_port0head = ipath_read_ureg32(dd, ur_rcvhdrtail, 0);
+
+       /* Initialize so we interrupt on next packet received */
+       (void)ipath_write_ureg(dd, ur_rcvhdrhead,
+                              dd->ipath_rhdrhead_intr_off |
+                              dd->ipath_port0head, 0);
+
+       /*
+        * by now pioavail updates to memory should have occurred, so
+        * copy them into our working/shadow registers; this is in
+        * case something went wrong with abort, but mostly to get the
+        * initial values of the generation bit correct.
+        */
+       for (i = 0; i < dd->ipath_pioavregs; i++) {
+               __le64 val;
+
+               /*
+                * Chip Errata bug 6641; even and odd qwords>3 are swapped.
+                */
+               if (i > 3) {
+                       if (i & 1)
+                               val = dd->ipath_pioavailregs_dma[i - 1];
+                       else
+                               val = dd->ipath_pioavailregs_dma[i + 1];
+               }
+               else
+                       val = dd->ipath_pioavailregs_dma[i];
+               dd->ipath_pioavailshadow[i] = le64_to_cpu(val);
+       }
+       /* can get counters, stats, etc. */
+       dd->ipath_flags |= IPATH_PRESENT;
+}
+
+static int init_housekeeping(struct ipath_devdata *dd,
+                            struct ipath_portdata **pdp, int reinit)
+{
+       char boardn[32];
+       int ret = 0;
+
+       /*
+        * have to clear shadow copies of registers at init that are
+        * not otherwise set here, or all kinds of bizarre things
+        * happen with driver on chip reset
+        */
+       dd->ipath_rcvhdrsize = 0;
+
+       /*
+        * Don't clear ipath_flags as 8bit mode was set before
+        * entering this func. However, we do set the linkstate to
+        * unknown, so we can watch for a transition.
+        */
+       dd->ipath_flags |= IPATH_LINKUNK;
+       dd->ipath_flags &= ~(IPATH_LINKACTIVE | IPATH_LINKARMED |
+                            IPATH_LINKDOWN | IPATH_LINKINIT);
+
+       ipath_cdbg(VERBOSE, "Try to read spc chip revision\n");
+       dd->ipath_revision =
+               ipath_read_kreg64(dd, dd->ipath_kregs->kr_revision);
+
+       /*
+        * set up fundamental info we need to use the chip; we assume
+        * if the revision reg and these regs are OK, we don't need to
+        * special case the rest
+        */
+       dd->ipath_sregbase =
+               ipath_read_kreg32(dd, dd->ipath_kregs->kr_sendregbase);
+       dd->ipath_cregbase =
+               ipath_read_kreg32(dd, dd->ipath_kregs->kr_counterregbase);
+       dd->ipath_uregbase =
+               ipath_read_kreg32(dd, dd->ipath_kregs->kr_userregbase);
+       ipath_cdbg(VERBOSE, "ipath_kregbase %p, sendbase %x usrbase %x, "
+                  "cntrbase %x\n", dd->ipath_kregbase, dd->ipath_sregbase,
+                  dd->ipath_uregbase, dd->ipath_cregbase);
+       if ((dd->ipath_revision & 0xffffffff) == 0xffffffff
+           || (dd->ipath_sregbase & 0xffffffff) == 0xffffffff
+           || (dd->ipath_cregbase & 0xffffffff) == 0xffffffff
+           || (dd->ipath_uregbase & 0xffffffff) == 0xffffffff) {
+               ipath_dev_err(dd, "Register read failures from chip, "
+                             "giving up initialization\n");
+               ret = -ENODEV;
+               goto done;
+       }
+
+       /* clear the initial reset flag, in case first driver load */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear,
+                        INFINIPATH_E_RESET);
+
+       if (reinit)
+               ret = init_chip_reset(dd, pdp);
+       else
+               ret = init_chip_first(dd, pdp);
+
+       if (ret)
+               goto done;
+
+       ipath_cdbg(VERBOSE, "Revision %llx (PCI %x), %u ports, %u tids, "
+                  "%u egrtids\n", (unsigned long long) dd->ipath_revision,
+                  dd->ipath_pcirev, dd->ipath_portcnt, dd->ipath_rcvtidcnt,
+                  dd->ipath_rcvegrcnt);
+
+       if (((dd->ipath_revision >> INFINIPATH_R_SOFTWARE_SHIFT) &
+            INFINIPATH_R_SOFTWARE_MASK) != IPATH_CHIP_SWVERSION) {
+               ipath_dev_err(dd, "Driver only handles version %d, "
+                             "chip swversion is %d (%llx), failng\n",
+                             IPATH_CHIP_SWVERSION,
+                             (int)(dd->ipath_revision >>
+                                   INFINIPATH_R_SOFTWARE_SHIFT) &
+                             INFINIPATH_R_SOFTWARE_MASK,
+                             (unsigned long long) dd->ipath_revision);
+               ret = -ENOSYS;
+               goto done;
+       }
+       dd->ipath_majrev = (u8) ((dd->ipath_revision >>
+                                 INFINIPATH_R_CHIPREVMAJOR_SHIFT) &
+                                INFINIPATH_R_CHIPREVMAJOR_MASK);
+       dd->ipath_minrev = (u8) ((dd->ipath_revision >>
+                                 INFINIPATH_R_CHIPREVMINOR_SHIFT) &
+                                INFINIPATH_R_CHIPREVMINOR_MASK);
+       dd->ipath_boardrev = (u8) ((dd->ipath_revision >>
+                                   INFINIPATH_R_BOARDID_SHIFT) &
+                                  INFINIPATH_R_BOARDID_MASK);
+
+       ret = dd->ipath_f_get_boardname(dd, boardn, sizeof boardn);
+
+       snprintf(dd->ipath_boardversion, sizeof(dd->ipath_boardversion),
+                "Driver %u.%u, %s, InfiniPath%u %u.%u, PCI %u, "
+                "SW Compat %u\n",
+                IPATH_CHIP_VERS_MAJ, IPATH_CHIP_VERS_MIN, boardn,
+                (unsigned)(dd->ipath_revision >> INFINIPATH_R_ARCH_SHIFT) &
+                INFINIPATH_R_ARCH_MASK,
+                dd->ipath_majrev, dd->ipath_minrev, dd->ipath_pcirev,
+                (unsigned)(dd->ipath_revision >>
+                           INFINIPATH_R_SOFTWARE_SHIFT) &
+                INFINIPATH_R_SOFTWARE_MASK);
+
+       ipath_dbg("%s", dd->ipath_boardversion);
+
+done:
+       return ret;
+}
+
+
+/**
+ * ipath_init_chip - do the actual initialization sequence on the chip
+ * @dd: the infinipath device
+ * @reinit: reinitializing, so don't allocate new memory
+ *
+ * Do the actual initialization sequence on the chip.  This is done
+ * both from the init routine called from the PCI infrastructure, and
+ * when we reset the chip, or detect that it was reset internally,
+ * or it's administratively re-enabled.
+ *
+ * Memory allocation here and in called routines is only done in
+ * the first case (reinit == 0).  We have to be careful, because even
+ * without memory allocation, we need to re-write all the chip registers
+ * TIDs, etc. after the reset or enable has completed.
+ */
+int ipath_init_chip(struct ipath_devdata *dd, int reinit)
+{
+       int ret = 0, i;
+       u32 val32, kpiobufs;
+       u64 val, atmp;
+       struct ipath_portdata *pd = NULL; /* keep gcc4 happy */
+
+       ret = init_housekeeping(dd, &pd, reinit);
+       if (ret)
+               goto done;
+
+       /*
+        * we ignore most issues after reporting them, but have to specially
+        * handle hardware-disabled chips.
+        */
+       if (ret == 2) {
+               /* unique error, known to ipath_init_one */
+               ret = -EPERM;
+               goto done;
+       }
+
+       /*
+        * We could bump this to allow for full rcvegrcnt + rcvtidcnt,
+        * but then it no longer nicely fits power of two, and since
+        * we now use routines that backend onto __get_free_pages, the
+        * rest would be wasted.
+        */
+       dd->ipath_rcvhdrcnt = dd->ipath_rcvegrcnt;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvhdrcnt,
+                        dd->ipath_rcvhdrcnt);
+
+       /*
+        * Set up the shadow copies of the piobufavail registers,
+        * which we compare against the chip registers for now, and
+        * the in memory DMA'ed copies of the registers.  This has to
+        * be done early, before we calculate lastport, etc.
+        */
+       val = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
+       /*
+        * calc number of pioavail registers, and save it; we have 2
+        * bits per buffer.
+        */
+       dd->ipath_pioavregs = ALIGN(val, sizeof(u64) * BITS_PER_BYTE / 2)
+               / (sizeof(u64) * BITS_PER_BYTE / 2);
+       if (!ipath_kpiobufs)    /* have to have at least 1, for SMA */
+               kpiobufs = ipath_kpiobufs = 1;
+       else if ((dd->ipath_piobcnt2k + dd->ipath_piobcnt4k) <
+                (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT)) {
+               dev_info(&dd->pcidev->dev, "Too few PIO buffers (%u) "
+                        "for %u ports to have %u each!\n",
+                        dd->ipath_piobcnt2k + dd->ipath_piobcnt4k,
+                        dd->ipath_cfgports, IPATH_MIN_USER_PORT_BUFCNT);
+               kpiobufs = 1;   /* reserve just the minimum for SMA/ether */
+       } else
+               kpiobufs = ipath_kpiobufs;
+
+       if (kpiobufs >
+           (dd->ipath_piobcnt2k + dd->ipath_piobcnt4k -
+            (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT))) {
+               i = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k -
+                       (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT);
+               if (i < 0)
+                       i = 0;
+               dev_info(&dd->pcidev->dev, "Allocating %d PIO bufs for "
+                        "kernel leaves too few for %d user ports "
+                        "(%d each); using %u\n", kpiobufs,
+                        dd->ipath_cfgports - 1,
+                        IPATH_MIN_USER_PORT_BUFCNT, i);
+               /*
+                * shouldn't change ipath_kpiobufs, because could be
+                * different for different devices...
+                */
+               kpiobufs = i;
+       }
+       dd->ipath_lastport_piobuf =
+               dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - kpiobufs;
+       dd->ipath_pbufsport = dd->ipath_cfgports > 1
+               ? dd->ipath_lastport_piobuf / (dd->ipath_cfgports - 1)
+               : 0;
+       val32 = dd->ipath_lastport_piobuf -
+               (dd->ipath_pbufsport * (dd->ipath_cfgports - 1));
+       if (val32 > 0) {
+               ipath_dbg("allocating %u pbufs/port leaves %u unused, "
+                         "add to kernel\n", dd->ipath_pbufsport, val32);
+               dd->ipath_lastport_piobuf -= val32;
+               ipath_dbg("%u pbufs/port leaves %u unused, add to kernel\n",
+                         dd->ipath_pbufsport, val32);
+       }
+       dd->ipath_lastpioindex = dd->ipath_lastport_piobuf;
+       ipath_cdbg(VERBOSE, "%d PIO bufs for kernel out of %d total %u "
+                  "each for %u user ports\n", kpiobufs,
+                  dd->ipath_piobcnt2k + dd->ipath_piobcnt4k,
+                  dd->ipath_pbufsport, dd->ipath_cfgports - 1);
+
+       dd->ipath_f_early_init(dd);
+
+       /* early_init sets rcvhdrentsize and rcvhdrsize, so this must be
+        * done after early_init */
+       dd->ipath_hdrqlast =
+               dd->ipath_rcvhdrentsize * (dd->ipath_rcvhdrcnt - 1);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvhdrentsize,
+                        dd->ipath_rcvhdrentsize);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvhdrsize,
+                        dd->ipath_rcvhdrsize);
+
+       if (!reinit) {
+               ret = init_pioavailregs(dd);
+               init_shadow_tids(dd);
+               if (ret)
+                       goto done;
+       }
+
+       (void)ipath_write_kreg(dd, dd->ipath_kregs->kr_sendpioavailaddr,
+                              dd->ipath_pioavailregs_phys);
+       /*
+        * this is to detect s/w errors, which the h/w works around by
+        * ignoring the low 6 bits of address, if it wasn't aligned.
+        */
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_sendpioavailaddr);
+       if (val != dd->ipath_pioavailregs_phys) {
+               ipath_dev_err(dd, "Catastrophic software error, "
+                             "SendPIOAvailAddr written as %lx, "
+                             "read back as %llx\n",
+                             (unsigned long) dd->ipath_pioavailregs_phys,
+                             (unsigned long long) val);
+               ret = -EINVAL;
+               goto done;
+       }
+
+       val = ipath_port0_rcvhdrtail_dma + dd->ipath_unit * 64;
+
+       /* verify that the alignment requirement was met */
+       ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdrtailaddr,
+                             0, val);
+       atmp = ipath_read_kreg64_port(
+               dd, dd->ipath_kregs->kr_rcvhdrtailaddr, 0);
+       if (val != atmp) {
+               ipath_dev_err(dd, "Catastrophic software error, "
+                             "RcvHdrTailAddr0 written as %llx, "
+                             "read back as %llx from %x\n",
+                             (unsigned long long) val,
+                             (unsigned long long) atmp,
+                             dd->ipath_kregs->kr_rcvhdrtailaddr);
+               ret = -EINVAL;
+               goto done;
+       }
+
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvbthqp, IPATH_KD_QP);
+
+       /*
+        * make sure we are not in freeze, and PIO send enabled, so
+        * writes to pbc happen
+        */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, 0ULL);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear,
+                        ~0ULL&~INFINIPATH_HWE_MEMBISTFAILED);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_control, 0ULL);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                        INFINIPATH_S_PIOENABLE);
+
+       /*
+        * before error clears, since we expect serdes pll errors during
+        * this, the first time after reset
+        */
+       if (bringup_link(dd)) {
+               dev_info(&dd->pcidev->dev, "Failed to bringup IB link\n");
+               ret = -ENETDOWN;
+               goto done;
+       }
+
+       /*
+        * clear any "expected" hwerrs from reset and/or initialization
+        * clear any that aren't enabled (at least this once), and then
+        * set the enable mask
+        */
+       dd->ipath_f_init_hwerrors(dd);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear,
+                        ~0ULL&~INFINIPATH_HWE_MEMBISTFAILED);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask,
+                        dd->ipath_hwerrmask);
+
+       dd->ipath_maskederrs = dd->ipath_ignorederrs;
+       /* clear all */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, -1LL);
+       /* enable errors that are masked, at least this first time. */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
+                        ~dd->ipath_maskederrs);
+       /* clear any interrups up to this point (ints still not enabled) */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, -1LL);
+
+       ipath_stats.sps_lid[dd->ipath_unit] = dd->ipath_lid;
+
+       /*
+        * Set up the port 0 (kernel) rcvhdr q and egr TIDs.  If doing
+        * re-init, the simplest way to handle this is to free
+        * existing, and re-allocate.
+        */
+       if (reinit)
+               ipath_free_pddata(dd, 0, 0);
+       dd->ipath_f_tidtemplate(dd);
+       ret = ipath_create_rcvhdrq(dd, pd);
+       if (!ret)
+               ret = create_port0_egr(dd);
+       if (ret)
+               ipath_dev_err(dd, "failed to allocate port 0 (kernel) "
+                             "rcvhdrq and/or egr bufs\n");
+       else
+               enable_chip(dd, pd, reinit);
+
+       /*
+        * cause retrigger of pending interrupts ignored during init,
+        * even if we had errors
+        */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, 0ULL);
+
+       if(!dd->ipath_stats_timer_active) {
+               /*
+                * first init, or after an admin disable/enable
+                * set up stats retrieval timer, even if we had errors
+                * in last portion of setup
+                */
+               init_timer(&dd->ipath_stats_timer);
+               dd->ipath_stats_timer.function = ipath_get_faststats;
+               dd->ipath_stats_timer.data = (unsigned long) dd;
+               /* every 5 seconds; */
+               dd->ipath_stats_timer.expires = jiffies + 5 * HZ;
+               /* takes ~16 seconds to overflow at full IB 4x bandwdith */
+               add_timer(&dd->ipath_stats_timer);
+               dd->ipath_stats_timer_active = 1;
+       }
+
+done:
+       if (!ret) {
+               ipath_get_guid(dd);
+               *dd->ipath_statusp |= IPATH_STATUS_CHIP_PRESENT;
+               if (!dd->ipath_f_intrsetup(dd)) {
+                       /* now we can enable all interrupts from the chip */
+                       ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask,
+                                        -1LL);
+                       /* force re-interrupt of any pending interrupts. */
+                       ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear,
+                                        0ULL);
+                       /* chip is usable; mark it as initialized */
+                       *dd->ipath_statusp |= IPATH_STATUS_INITTED;
+               } else
+                       ipath_dev_err(dd, "No interrupts enabled, couldn't "
+                                     "setup interrupt address\n");
+
+               if (dd->ipath_cfgports > ipath_stats.sps_nports)
+                       /*
+                        * sps_nports is a global, so, we set it to
+                        * the highest number of ports of any of the
+                        * chips we find; we never decrement it, at
+                        * least for now.  Since this might have changed
+                        * over disable/enable or prior to reset, always
+                        * do the check and potentially adjust.
+                        */
+                       ipath_stats.sps_nports = dd->ipath_cfgports;
+       } else
+               ipath_dbg("Failed (%d) to initialize chip\n", ret);
+
+       /* if ret is non-zero, we probably should do some cleanup
+          here... */
+       return ret;
+}
+
+static int ipath_set_kpiobufs(const char *str, struct kernel_param *kp)
+{
+       struct ipath_devdata *dd;
+       unsigned long flags;
+       unsigned short val;
+       int ret;
+
+       ret = ipath_parse_ushort(str, &val);
+
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+
+       if (ret < 0)
+               goto bail;
+
+       if (val == 0) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       list_for_each_entry(dd, &ipath_dev_list, ipath_list) {
+               if (dd->ipath_kregbase)
+                       continue;
+               if (val > (dd->ipath_piobcnt2k + dd->ipath_piobcnt4k -
+                          (dd->ipath_cfgports *
+                           IPATH_MIN_USER_PORT_BUFCNT)))
+               {
+                       ipath_dev_err(
+                               dd,
+                               "Allocating %d PIO bufs for kernel leaves "
+                               "too few for %d user ports (%d each)\n",
+                               val, dd->ipath_cfgports - 1,
+                               IPATH_MIN_USER_PORT_BUFCNT);
+                       ret = -EINVAL;
+                       goto bail;
+               }
+               dd->ipath_lastport_piobuf =
+                       dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - val;
+       }
+
+       ret = 0;
+bail:
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+
+       return ret;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c
new file mode 100644 (file)
index 0000000..60f5f41
--- /dev/null
@@ -0,0 +1,841 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/pci.h>
+
+#include "ipath_kernel.h"
+#include "ips_common.h"
+#include "ipath_layer.h"
+
+#define E_SUM_PKTERRS \
+       (INFINIPATH_E_RHDRLEN | INFINIPATH_E_RBADTID | \
+        INFINIPATH_E_RBADVERSION | INFINIPATH_E_RHDR | \
+        INFINIPATH_E_RLONGPKTLEN | INFINIPATH_E_RSHORTPKTLEN | \
+        INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RMINPKTLEN | \
+        INFINIPATH_E_RFORMATERR | INFINIPATH_E_RUNSUPVL | \
+        INFINIPATH_E_RUNEXPCHAR | INFINIPATH_E_REBP)
+
+#define E_SUM_ERRS \
+       (INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SUNEXPERRPKTNUM | \
+        INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_SDROPPEDSMPPKT | \
+        INFINIPATH_E_SMAXPKTLEN | INFINIPATH_E_SUNSUPVL | \
+        INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SPKTLEN | \
+        INFINIPATH_E_INVALIDADDR)
+
+static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs)
+{
+       unsigned long sbuf[4];
+       u64 ignore_this_time = 0;
+       u32 piobcnt;
+
+       /* if possible that sendbuffererror could be valid */
+       piobcnt = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
+       /* read these before writing errorclear */
+       sbuf[0] = ipath_read_kreg64(
+               dd, dd->ipath_kregs->kr_sendbuffererror);
+       sbuf[1] = ipath_read_kreg64(
+               dd, dd->ipath_kregs->kr_sendbuffererror + 1);
+       if (piobcnt > 128) {
+               sbuf[2] = ipath_read_kreg64(
+                       dd, dd->ipath_kregs->kr_sendbuffererror + 2);
+               sbuf[3] = ipath_read_kreg64(
+                       dd, dd->ipath_kregs->kr_sendbuffererror + 3);
+       }
+
+       if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) {
+               int i;
+
+               ipath_cdbg(PKT, "SendbufErrs %lx %lx ", sbuf[0], sbuf[1]);
+               if (ipath_debug & __IPATH_PKTDBG && piobcnt > 128)
+                       printk("%lx %lx ", sbuf[2], sbuf[3]);
+               for (i = 0; i < piobcnt; i++) {
+                       if (test_bit(i, sbuf)) {
+                               u32 __iomem *piobuf;
+                               if (i < dd->ipath_piobcnt2k)
+                                       piobuf = (u32 __iomem *)
+                                               (dd->ipath_pio2kbase +
+                                                i * dd->ipath_palign);
+                               else
+                                       piobuf = (u32 __iomem *)
+                                               (dd->ipath_pio4kbase +
+                                                (i - dd->ipath_piobcnt2k) *
+                                                dd->ipath_4kalign);
+
+                               ipath_cdbg(PKT,
+                                          "PIObuf[%u] @%p pbc is %x; ",
+                                          i, piobuf, readl(piobuf));
+
+                               ipath_disarm_piobufs(dd, i, 1);
+                       }
+               }
+               if (ipath_debug & __IPATH_PKTDBG)
+                       printk("\n");
+       }
+       if ((errs & (INFINIPATH_E_SDROPPEDDATAPKT |
+                    INFINIPATH_E_SDROPPEDSMPPKT |
+                    INFINIPATH_E_SMINPKTLEN)) &&
+           !(dd->ipath_flags & IPATH_LINKACTIVE)) {
+               /*
+                * This can happen when SMA is trying to bring the link
+                * up, but the IB link changes state at the "wrong" time.
+                * The IB logic then complains that the packet isn't
+                * valid.  We don't want to confuse people, so we just
+                * don't print them, except at debug
+                */
+               ipath_dbg("Ignoring pktsend errors %llx, because not "
+                         "yet active\n", (unsigned long long) errs);
+               ignore_this_time = INFINIPATH_E_SDROPPEDDATAPKT |
+                       INFINIPATH_E_SDROPPEDSMPPKT |
+                       INFINIPATH_E_SMINPKTLEN;
+       }
+
+       return ignore_this_time;
+}
+
+/* return the strings for the most common link states */
+static char *ib_linkstate(u32 linkstate)
+{
+       char *ret;
+
+       switch (linkstate) {
+       case IPATH_IBSTATE_INIT:
+               ret = "Init";
+               break;
+       case IPATH_IBSTATE_ARM:
+               ret = "Arm";
+               break;
+       case IPATH_IBSTATE_ACTIVE:
+               ret = "Active";
+               break;
+       default:
+               ret = "Down";
+       }
+
+       return ret;
+}
+
+static void handle_e_ibstatuschanged(struct ipath_devdata *dd,
+                                    ipath_err_t errs, int noprint)
+{
+       u64 val;
+       u32 ltstate, lstate;
+
+       /*
+        * even if diags are enabled, we want to notice LINKINIT, etc.
+        * We just don't want to change the LED state, or
+        * dd->ipath_kregs->kr_ibcctrl
+        */
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_ibcstatus);
+       lstate = val & IPATH_IBSTATE_MASK;
+       if (lstate == IPATH_IBSTATE_INIT || lstate == IPATH_IBSTATE_ARM ||
+           lstate == IPATH_IBSTATE_ACTIVE) {
+               /*
+                * only print at SMA if there is a change, debug if not
+                * (sometimes we want to know that, usually not).
+                */
+               if (lstate == ((unsigned) dd->ipath_lastibcstat
+                              & IPATH_IBSTATE_MASK)) {
+                       ipath_dbg("Status change intr but no change (%s)\n",
+                                 ib_linkstate(lstate));
+               }
+               else
+                       ipath_cdbg(SMA, "Unit %u link state %s, last "
+                                  "was %s\n", dd->ipath_unit,
+                                  ib_linkstate(lstate),
+                                  ib_linkstate((unsigned)
+                                               dd->ipath_lastibcstat
+                                               & IPATH_IBSTATE_MASK));
+       }
+       else {
+               lstate = dd->ipath_lastibcstat & IPATH_IBSTATE_MASK;
+               if (lstate == IPATH_IBSTATE_INIT ||
+                   lstate == IPATH_IBSTATE_ARM ||
+                   lstate == IPATH_IBSTATE_ACTIVE)
+                       ipath_cdbg(SMA, "Unit %u link state down"
+                                  " (state 0x%x), from %s\n",
+                                  dd->ipath_unit,
+                                  (u32)val & IPATH_IBSTATE_MASK,
+                                  ib_linkstate(lstate));
+               else
+                       ipath_cdbg(VERBOSE, "Unit %u link state changed "
+                                  "to 0x%x from down (%x)\n",
+                                  dd->ipath_unit, (u32) val, lstate);
+       }
+       ltstate = (val >> INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) &
+               INFINIPATH_IBCS_LINKTRAININGSTATE_MASK;
+       lstate = (val >> INFINIPATH_IBCS_LINKSTATE_SHIFT) &
+               INFINIPATH_IBCS_LINKSTATE_MASK;
+
+       if (ltstate == INFINIPATH_IBCS_LT_STATE_POLLACTIVE ||
+           ltstate == INFINIPATH_IBCS_LT_STATE_POLLQUIET) {
+               u32 last_ltstate;
+
+               /*
+                * Ignore cycling back and forth from Polling.Active
+                * to Polling.Quiet while waiting for the other end of
+                * the link to come up. We will cycle back and forth
+                * between them if no cable is plugged in,
+                * the other device is powered off or disabled, etc.
+                */
+               last_ltstate = (dd->ipath_lastibcstat >>
+                               INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT)
+                       & INFINIPATH_IBCS_LINKTRAININGSTATE_MASK;
+               if (last_ltstate == INFINIPATH_IBCS_LT_STATE_POLLACTIVE
+                   || last_ltstate ==
+                   INFINIPATH_IBCS_LT_STATE_POLLQUIET) {
+                       if (dd->ipath_ibpollcnt > 40) {
+                               dd->ipath_flags |= IPATH_NOCABLE;
+                               *dd->ipath_statusp |=
+                                       IPATH_STATUS_IB_NOCABLE;
+                       } else
+                               dd->ipath_ibpollcnt++;
+                       goto skip_ibchange;
+               }
+       }
+       dd->ipath_ibpollcnt = 0;        /* some state other than 2 or 3 */
+       ipath_stats.sps_iblink++;
+       if (ltstate != INFINIPATH_IBCS_LT_STATE_LINKUP) {
+               dd->ipath_flags |= IPATH_LINKDOWN;
+               dd->ipath_flags &= ~(IPATH_LINKUNK | IPATH_LINKINIT
+                                    | IPATH_LINKACTIVE |
+                                    IPATH_LINKARMED);
+               *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
+               if (!noprint) {
+                       if (((dd->ipath_lastibcstat >>
+                             INFINIPATH_IBCS_LINKSTATE_SHIFT) &
+                            INFINIPATH_IBCS_LINKSTATE_MASK)
+                           == INFINIPATH_IBCS_L_STATE_ACTIVE)
+                               /* if from up to down be more vocal */
+                               ipath_cdbg(SMA,
+                                          "Unit %u link now down (%s)\n",
+                                          dd->ipath_unit,
+                                          ipath_ibcstatus_str[ltstate]);
+                       else
+                               ipath_cdbg(VERBOSE, "Unit %u link is "
+                                          "down (%s)\n", dd->ipath_unit,
+                                          ipath_ibcstatus_str[ltstate]);
+               }
+
+               dd->ipath_f_setextled(dd, lstate, ltstate);
+       } else if ((val & IPATH_IBSTATE_MASK) == IPATH_IBSTATE_ACTIVE) {
+               dd->ipath_flags |= IPATH_LINKACTIVE;
+               dd->ipath_flags &=
+                       ~(IPATH_LINKUNK | IPATH_LINKINIT | IPATH_LINKDOWN |
+                         IPATH_LINKARMED | IPATH_NOCABLE);
+               *dd->ipath_statusp &= ~IPATH_STATUS_IB_NOCABLE;
+               *dd->ipath_statusp |=
+                       IPATH_STATUS_IB_READY | IPATH_STATUS_IB_CONF;
+               dd->ipath_f_setextled(dd, lstate, ltstate);
+
+               __ipath_layer_intr(dd, IPATH_LAYER_INT_IF_UP);
+       } else if ((val & IPATH_IBSTATE_MASK) == IPATH_IBSTATE_INIT) {
+               /*
+                * set INIT and DOWN.  Down is checked by most of the other
+                * code, but INIT is useful to know in a few places.
+                */
+               dd->ipath_flags |= IPATH_LINKINIT | IPATH_LINKDOWN;
+               dd->ipath_flags &=
+                       ~(IPATH_LINKUNK | IPATH_LINKACTIVE | IPATH_LINKARMED
+                         | IPATH_NOCABLE);
+               *dd->ipath_statusp &= ~(IPATH_STATUS_IB_NOCABLE
+                                       | IPATH_STATUS_IB_READY);
+               dd->ipath_f_setextled(dd, lstate, ltstate);
+       } else if ((val & IPATH_IBSTATE_MASK) == IPATH_IBSTATE_ARM) {
+               dd->ipath_flags |= IPATH_LINKARMED;
+               dd->ipath_flags &=
+                       ~(IPATH_LINKUNK | IPATH_LINKDOWN | IPATH_LINKINIT |
+                         IPATH_LINKACTIVE | IPATH_NOCABLE);
+               *dd->ipath_statusp &= ~(IPATH_STATUS_IB_NOCABLE
+                                       | IPATH_STATUS_IB_READY);
+               dd->ipath_f_setextled(dd, lstate, ltstate);
+       } else {
+               if (!noprint)
+                       ipath_dbg("IBstatuschange unit %u: %s (%x)\n",
+                                 dd->ipath_unit,
+                                 ipath_ibcstatus_str[ltstate], ltstate);
+       }
+skip_ibchange:
+       dd->ipath_lastibcstat = val;
+}
+
+static void handle_supp_msgs(struct ipath_devdata *dd,
+                            unsigned supp_msgs, char msg[512])
+{
+       /*
+        * Print the message unless it's ibc status change only, which
+        * happens so often we never want to count it.
+        */
+       if (dd->ipath_lasterror & ~INFINIPATH_E_IBSTATUSCHANGED) {
+               ipath_decode_err(msg, sizeof msg, dd->ipath_lasterror &
+                                ~INFINIPATH_E_IBSTATUSCHANGED);
+               if (dd->ipath_lasterror &
+                   ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL))
+                       ipath_dev_err(dd, "Suppressed %u messages for "
+                                     "fast-repeating errors (%s) (%llx)\n",
+                                     supp_msgs, msg,
+                                     (unsigned long long)
+                                     dd->ipath_lasterror);
+               else {
+                       /*
+                        * rcvegrfull and rcvhdrqfull are "normal", for some
+                        * types of processes (mostly benchmarks) that send
+                        * huge numbers of messages, while not processing
+                        * them. So only complain about these at debug
+                        * level.
+                        */
+                       ipath_dbg("Suppressed %u messages for %s\n",
+                                 supp_msgs, msg);
+               }
+       }
+}
+
+static unsigned handle_frequent_errors(struct ipath_devdata *dd,
+                                      ipath_err_t errs, char msg[512],
+                                      int *noprint)
+{
+       unsigned long nc;
+       static unsigned long nextmsg_time;
+       static unsigned nmsgs, supp_msgs;
+
+       /*
+        * Throttle back "fast" messages to no more than 10 per 5 seconds.
+        * This isn't perfect, but it's a reasonable heuristic. If we get
+        * more than 10, give a 6x longer delay.
+        */
+       nc = jiffies;
+       if (nmsgs > 10) {
+               if (time_before(nc, nextmsg_time)) {
+                       *noprint = 1;
+                       if (!supp_msgs++)
+                               nextmsg_time = nc + HZ * 3;
+               }
+               else if (supp_msgs) {
+                       handle_supp_msgs(dd, supp_msgs, msg);
+                       supp_msgs = 0;
+                       nmsgs = 0;
+               }
+       }
+       else if (!nmsgs++ || time_after(nc, nextmsg_time))
+               nextmsg_time = nc + HZ / 2;
+
+       return supp_msgs;
+}
+
+static void handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
+{
+       char msg[512];
+       u64 ignore_this_time = 0;
+       int i;
+       int chkerrpkts = 0, noprint = 0;
+       unsigned supp_msgs;
+
+       supp_msgs = handle_frequent_errors(dd, errs, msg, &noprint);
+
+       /*
+        * don't report errors that are masked (includes those always
+        * ignored)
+        */
+       errs &= ~dd->ipath_maskederrs;
+
+       /* do these first, they are most important */
+       if (errs & INFINIPATH_E_HARDWARE) {
+               /* reuse same msg buf */
+               dd->ipath_f_handle_hwerrors(dd, msg, sizeof msg);
+       }
+
+       if (!noprint && (errs & ~infinipath_e_bitsextant))
+               ipath_dev_err(dd, "error interrupt with unknown errors "
+                             "%llx set\n", (unsigned long long)
+                             (errs & ~infinipath_e_bitsextant));
+
+       if (errs & E_SUM_ERRS)
+               ignore_this_time = handle_e_sum_errs(dd, errs);
+
+       if (supp_msgs == 250000) {
+               /*
+                * It's not entirely reasonable assuming that the errors set
+                * in the last clear period are all responsible for the
+                * problem, but the alternative is to assume it's the only
+                * ones on this particular interrupt, which also isn't great
+                */
+               dd->ipath_maskederrs |= dd->ipath_lasterror | errs;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
+                                ~dd->ipath_maskederrs);
+               ipath_decode_err(msg, sizeof msg,
+                                (dd->ipath_maskederrs & ~dd->
+                                 ipath_ignorederrs));
+
+               if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) &
+                   ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL))
+                       ipath_dev_err(dd, "Disabling error(s) %llx because "
+                                     "occuring too frequently (%s)\n",
+                                     (unsigned long long)
+                                     (dd->ipath_maskederrs &
+                                      ~dd->ipath_ignorederrs), msg);
+               else {
+                       /*
+                        * rcvegrfull and rcvhdrqfull are "normal",
+                        * for some types of processes (mostly benchmarks)
+                        * that send huge numbers of messages, while not
+                        * processing them.  So only complain about
+                        * these at debug level.
+                        */
+                       ipath_dbg("Disabling frequent queue full errors "
+                                 "(%s)\n", msg);
+               }
+
+               /*
+                * Re-enable the masked errors after around 3 minutes.  in
+                * ipath_get_faststats().  If we have a series of fast
+                * repeating but different errors, the interval will keep
+                * stretching out, but that's OK, as that's pretty
+                * catastrophic.
+                */
+               dd->ipath_unmasktime = jiffies + HZ * 180;
+       }
+
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, errs);
+       if (ignore_this_time)
+               errs &= ~ignore_this_time;
+       if (errs & ~dd->ipath_lasterror) {
+               errs &= ~dd->ipath_lasterror;
+               /* never suppress duplicate hwerrors or ibstatuschange */
+               dd->ipath_lasterror |= errs &
+                       ~(INFINIPATH_E_HARDWARE |
+                         INFINIPATH_E_IBSTATUSCHANGED);
+       }
+       if (!errs)
+               return;
+
+       if (!noprint)
+               /*
+                * the ones we mask off are handled specially below or above
+                */
+               ipath_decode_err(msg, sizeof msg,
+                                errs & ~(INFINIPATH_E_IBSTATUSCHANGED |
+                                         INFINIPATH_E_RRCVEGRFULL |
+                                         INFINIPATH_E_RRCVHDRFULL |
+                                         INFINIPATH_E_HARDWARE));
+       else
+               /* so we don't need if (!noprint) at strlcat's below */
+               *msg = 0;
+
+       if (errs & E_SUM_PKTERRS) {
+               ipath_stats.sps_pkterrs++;
+               chkerrpkts = 1;
+       }
+       if (errs & E_SUM_ERRS)
+               ipath_stats.sps_errs++;
+
+       if (errs & (INFINIPATH_E_RICRC | INFINIPATH_E_RVCRC)) {
+               ipath_stats.sps_crcerrs++;
+               chkerrpkts = 1;
+       }
+
+       /*
+        * We don't want to print these two as they happen, or we can make
+        * the situation even worse, because it takes so long to print
+        * messages to serial consoles.  Kernel ports get printed from
+        * fast_stats, no more than every 5 seconds, user ports get printed
+        * on close
+        */
+       if (errs & INFINIPATH_E_RRCVHDRFULL) {
+               int any;
+               u32 hd, tl;
+               ipath_stats.sps_hdrqfull++;
+               for (any = i = 0; i < dd->ipath_cfgports; i++) {
+                       struct ipath_portdata *pd = dd->ipath_pd[i];
+                       if (i == 0) {
+                               hd = dd->ipath_port0head;
+                               tl = (u32) le64_to_cpu(
+                                       *dd->ipath_hdrqtailptr);
+                       } else if (pd && pd->port_cnt &&
+                                  pd->port_rcvhdrtail_kvaddr) {
+                               /*
+                                * don't report same point multiple times,
+                                * except kernel
+                                */
+                               tl = (u32) * pd->port_rcvhdrtail_kvaddr;
+                               if (tl == dd->ipath_lastrcvhdrqtails[i])
+                                       continue;
+                               hd = ipath_read_ureg32(dd, ur_rcvhdrhead,
+                                                      i);
+                       } else
+                               continue;
+                       if (hd == (tl + 1) ||
+                           (!hd && tl == dd->ipath_hdrqlast)) {
+                               dd->ipath_lastrcvhdrqtails[i] = tl;
+                               pd->port_hdrqfull++;
+                               if (i == 0)
+                                       chkerrpkts = 1;
+                       }
+               }
+       }
+       if (errs & INFINIPATH_E_RRCVEGRFULL) {
+               /*
+                * since this is of less importance and not likely to
+                * happen without also getting hdrfull, only count
+                * occurrences; don't check each port (or even the kernel
+                * vs user)
+                */
+               ipath_stats.sps_etidfull++;
+               if (dd->ipath_port0head !=
+                   (u32) le64_to_cpu(*dd->ipath_hdrqtailptr))
+                       chkerrpkts = 1;
+       }
+
+       /*
+        * do this before IBSTATUSCHANGED, in case both bits set in a single
+        * interrupt; we want the STATUSCHANGE to "win", so we do our
+        * internal copy of state machine correctly
+        */
+       if (errs & INFINIPATH_E_RIBLOSTLINK) {
+               /*
+                * force through block below
+                */
+               errs |= INFINIPATH_E_IBSTATUSCHANGED;
+               ipath_stats.sps_iblink++;
+               dd->ipath_flags |= IPATH_LINKDOWN;
+               dd->ipath_flags &= ~(IPATH_LINKUNK | IPATH_LINKINIT
+                                    | IPATH_LINKARMED | IPATH_LINKACTIVE);
+               *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
+               if (!noprint) {
+                       u64 st = ipath_read_kreg64(
+                               dd, dd->ipath_kregs->kr_ibcstatus);
+
+                       ipath_dbg("Lost link, link now down (%s)\n",
+                                 ipath_ibcstatus_str[st & 0xf]);
+               }
+       }
+       if (errs & INFINIPATH_E_IBSTATUSCHANGED)
+               handle_e_ibstatuschanged(dd, errs, noprint);
+
+       if (errs & INFINIPATH_E_RESET) {
+               if (!noprint)
+                       ipath_dev_err(dd, "Got reset, requires re-init "
+                                     "(unload and reload driver)\n");
+               dd->ipath_flags &= ~IPATH_INITTED;      /* needs re-init */
+               /* mark as having had error */
+               *dd->ipath_statusp |= IPATH_STATUS_HWERROR;
+               *dd->ipath_statusp &= ~IPATH_STATUS_IB_CONF;
+       }
+
+       if (!noprint && *msg)
+               ipath_dev_err(dd, "%s error\n", msg);
+       if (dd->ipath_sma_state_wanted & dd->ipath_flags) {
+               ipath_cdbg(VERBOSE, "sma wanted state %x, iflags now %x, "
+                          "waking\n", dd->ipath_sma_state_wanted,
+                          dd->ipath_flags);
+               wake_up_interruptible(&ipath_sma_state_wait);
+       }
+
+       if (chkerrpkts)
+               /* process possible error packets in hdrq */
+               ipath_kreceive(dd);
+}
+
+/* this is separate to allow for better optimization of ipath_intr() */
+
+static void ipath_bad_intr(struct ipath_devdata *dd, u32 * unexpectp)
+{
+       /*
+        * sometimes happen during driver init and unload, don't want
+        * to process any interrupts at that point
+        */
+
+       /* this is just a bandaid, not a fix, if something goes badly
+        * wrong */
+       if (++*unexpectp > 100) {
+               if (++*unexpectp > 105) {
+                       /*
+                        * ok, we must be taking somebody else's interrupts,
+                        * due to a messed up mptable and/or PIRQ table, so
+                        * unregister the interrupt.  We've seen this during
+                        * linuxbios development work, and it may happen in
+                        * the future again.
+                        */
+                       if (dd->pcidev && dd->pcidev->irq) {
+                               ipath_dev_err(dd, "Now %u unexpected "
+                                             "interrupts, unregistering "
+                                             "interrupt handler\n",
+                                             *unexpectp);
+                               ipath_dbg("free_irq of irq %x\n",
+                                         dd->pcidev->irq);
+                               free_irq(dd->pcidev->irq, dd);
+                       }
+               }
+               if (ipath_read_kreg32(dd, dd->ipath_kregs->kr_intmask)) {
+                       ipath_dev_err(dd, "%u unexpected interrupts, "
+                                     "disabling interrupts completely\n",
+                                     *unexpectp);
+                       /*
+                        * disable all interrupts, something is very wrong
+                        */
+                       ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask,
+                                        0ULL);
+               }
+       } else if (*unexpectp > 1)
+               ipath_dbg("Interrupt when not ready, should not happen, "
+                         "ignoring\n");
+}
+
+static void ipath_bad_regread(struct ipath_devdata *dd)
+{
+       static int allbits;
+
+       /* separate routine, for better optimization of ipath_intr() */
+
+       /*
+        * We print the message and disable interrupts, in hope of
+        * having a better chance of debugging the problem.
+        */
+       ipath_dev_err(dd,
+                     "Read of interrupt status failed (all bits set)\n");
+       if (allbits++) {
+               /* disable all interrupts, something is very wrong */
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask, 0ULL);
+               if (allbits == 2) {
+                       ipath_dev_err(dd, "Still bad interrupt status, "
+                                     "unregistering interrupt\n");
+                       free_irq(dd->pcidev->irq, dd);
+               } else if (allbits > 2) {
+                       if ((allbits % 10000) == 0)
+                               printk(".");
+               } else
+                       ipath_dev_err(dd, "Disabling interrupts, "
+                                     "multiple errors\n");
+       }
+}
+
+static void handle_port_pioavail(struct ipath_devdata *dd)
+{
+       u32 i;
+       /*
+        * start from port 1, since for now port 0  is never using
+        * wait_event for PIO
+        */
+       for (i = 1; dd->ipath_portpiowait && i < dd->ipath_cfgports; i++) {
+               struct ipath_portdata *pd = dd->ipath_pd[i];
+
+               if (pd && pd->port_cnt &&
+                   dd->ipath_portpiowait & (1U << i)) {
+                       clear_bit(i, &dd->ipath_portpiowait);
+                       if (test_bit(IPATH_PORT_WAITING_PIO,
+                                    &pd->port_flag)) {
+                               clear_bit(IPATH_PORT_WAITING_PIO,
+                                         &pd->port_flag);
+                               wake_up_interruptible(&pd->port_wait);
+                       }
+               }
+       }
+}
+
+static void handle_layer_pioavail(struct ipath_devdata *dd)
+{
+       int ret;
+
+       ret = __ipath_layer_intr(dd, IPATH_LAYER_INT_SEND_CONTINUE);
+       if (ret > 0)
+               goto clear;
+
+       ret = __ipath_verbs_piobufavail(dd);
+       if (ret > 0)
+               goto clear;
+
+       return;
+clear:
+       set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                        dd->ipath_sendctrl);
+}
+
+static void handle_rcv(struct ipath_devdata *dd, u32 istat)
+{
+       u64 portr;
+       int i;
+       int rcvdint = 0;
+
+       portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) &
+                infinipath_i_rcvavail_mask)
+               | ((istat >> INFINIPATH_I_RCVURG_SHIFT) &
+                  infinipath_i_rcvurg_mask);
+       for (i = 0; i < dd->ipath_cfgports; i++) {
+               struct ipath_portdata *pd = dd->ipath_pd[i];
+               if (portr & (1 << i) && pd &&
+                   pd->port_cnt) {
+                       if (i == 0)
+                               ipath_kreceive(dd);
+                       else if (test_bit(IPATH_PORT_WAITING_RCV,
+                                         &pd->port_flag)) {
+                               int rcbit;
+                               clear_bit(IPATH_PORT_WAITING_RCV,
+                                         &pd->port_flag);
+                               rcbit = i + INFINIPATH_R_INTRAVAIL_SHIFT;
+                               clear_bit(1UL << rcbit, &dd->ipath_rcvctrl);
+                               wake_up_interruptible(&pd->port_wait);
+                               rcvdint = 1;
+                       }
+               }
+       }
+       if (rcvdint) {
+               /* only want to take one interrupt, so turn off the rcv
+                * interrupt for all the ports that we did the wakeup on
+                * (but never for kernel port)
+                */
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+                                dd->ipath_rcvctrl);
+       }
+}
+
+irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
+{
+       struct ipath_devdata *dd = data;
+       u32 istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus);
+       ipath_err_t estat = 0;
+       static unsigned unexpected = 0;
+       irqreturn_t ret;
+
+       if (unlikely(!istat)) {
+               ipath_stats.sps_nullintr++;
+               ret = IRQ_NONE; /* not our interrupt, or already handled */
+               goto bail;
+       }
+       if (unlikely(istat == -1)) {
+               ipath_bad_regread(dd);
+               /* don't know if it was our interrupt or not */
+               ret = IRQ_NONE;
+               goto bail;
+       }
+
+       ipath_stats.sps_ints++;
+
+       /*
+        * this needs to be flags&initted, not statusp, so we keep
+        * taking interrupts even after link goes down, etc.
+        * Also, we *must* clear the interrupt at some point, or we won't
+        * take it again, which can be real bad for errors, etc...
+        */
+
+       if (!(dd->ipath_flags & IPATH_INITTED)) {
+               ipath_bad_intr(dd, &unexpected);
+               ret = IRQ_NONE;
+               goto bail;
+       }
+       if (unexpected)
+               unexpected = 0;
+
+       ipath_cdbg(VERBOSE, "intr stat=0x%x\n", istat);
+
+       if (istat & ~infinipath_i_bitsextant)
+               ipath_dev_err(dd,
+                             "interrupt with unknown interrupts %x set\n",
+                             istat & (u32) ~ infinipath_i_bitsextant);
+
+       if (istat & INFINIPATH_I_ERROR) {
+               ipath_stats.sps_errints++;
+               estat = ipath_read_kreg64(dd,
+                                         dd->ipath_kregs->kr_errorstatus);
+               if (!estat)
+                       dev_info(&dd->pcidev->dev, "error interrupt (%x), "
+                                "but no error bits set!\n", istat);
+               else if (estat == -1LL)
+                       /*
+                        * should we try clearing all, or hope next read
+                        * works?
+                        */
+                       ipath_dev_err(dd, "Read of error status failed "
+                                     "(all bits set); ignoring\n");
+               else
+                       handle_errors(dd, estat);
+       }
+
+       if (istat & INFINIPATH_I_GPIO) {
+               if (unlikely(!(dd->ipath_flags & IPATH_GPIO_INTR))) {
+                       u32 gpiostatus;
+                       gpiostatus = ipath_read_kreg32(
+                               dd, dd->ipath_kregs->kr_gpio_status);
+                       ipath_dbg("Unexpected GPIO interrupt bits %x\n",
+                                 gpiostatus);
+                       ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
+                                        gpiostatus);
+               }
+               else {
+                       /* Clear GPIO status bit 2 */
+                       ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
+                                        (u64) (1 << 2));
+
+                       /*
+                        * Packets are available in the port 0 rcv queue.
+                        * Eventually this needs to be generalized to check
+                        * IPATH_GPIO_INTR, and the specific GPIO bit, if
+                        * GPIO interrupts are used for anything else.
+                        */
+                       ipath_kreceive(dd);
+               }
+       }
+
+       /*
+        * clear the ones we will deal with on this round
+        * We clear it early, mostly for receive interrupts, so we
+        * know the chip will have seen this by the time we process
+        * the queue, and will re-interrupt if necessary.  The processor
+        * itself won't take the interrupt again until we return.
+        */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat);
+
+       if (istat & INFINIPATH_I_SPIOBUFAVAIL) {
+               clear_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                                dd->ipath_sendctrl);
+
+               if (dd->ipath_portpiowait)
+                       handle_port_pioavail(dd);
+
+               handle_layer_pioavail(dd);
+       }
+
+       /*
+        * we check for both transition from empty to non-empty, and urgent
+        * packets (those with the interrupt bit set in the header)
+        */
+
+       if (istat & ((infinipath_i_rcvavail_mask <<
+                     INFINIPATH_I_RCVAVAIL_SHIFT)
+                    | (infinipath_i_rcvurg_mask <<
+                       INFINIPATH_I_RCVURG_SHIFT)))
+               handle_rcv(dd, istat);
+
+       ret = IRQ_HANDLED;
+
+bail:
+       return ret;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h
new file mode 100644 (file)
index 0000000..159d0ae
--- /dev/null
@@ -0,0 +1,884 @@
+#ifndef _IPATH_KERNEL_H
+#define _IPATH_KERNEL_H
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * This header file is the base header file for infinipath kernel code
+ * ipath_user.h serves a similar purpose for user code.
+ */
+
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include "ipath_common.h"
+#include "ipath_debug.h"
+#include "ipath_registers.h"
+
+/* only s/w major version of InfiniPath we can handle */
+#define IPATH_CHIP_VERS_MAJ 2U
+
+/* don't care about this except printing */
+#define IPATH_CHIP_VERS_MIN 0U
+
+/* temporary, maybe always */
+extern struct infinipath_stats ipath_stats;
+
+#define IPATH_CHIP_SWVERSION IPATH_CHIP_VERS_MAJ
+
+struct ipath_portdata {
+       void **port_rcvegrbuf;
+       dma_addr_t *port_rcvegrbuf_phys;
+       /* rcvhdrq base, needs mmap before useful */
+       void *port_rcvhdrq;
+       /* kernel virtual address where hdrqtail is updated */
+       u64 *port_rcvhdrtail_kvaddr;
+       /* page * used for uaddr */
+       struct page *port_rcvhdrtail_pagep;
+       /*
+        * temp buffer for expected send setup, allocated at open, instead
+        * of each setup call
+        */
+       void *port_tid_pg_list;
+       /* when waiting for rcv or pioavail */
+       wait_queue_head_t port_wait;
+       /*
+        * rcvegr bufs base, physical, must fit
+        * in 44 bits so 32 bit programs mmap64 44 bit works)
+        */
+       dma_addr_t port_rcvegr_phys;
+       /* mmap of hdrq, must fit in 44 bits */
+       dma_addr_t port_rcvhdrq_phys;
+       /*
+        * the actual user address that we ipath_mlock'ed, so we can
+        * ipath_munlock it at close
+        */
+       unsigned long port_rcvhdrtail_uaddr;
+       /*
+        * number of opens on this instance (0 or 1; ignoring forks, dup,
+        * etc. for now)
+        */
+       int port_cnt;
+       /*
+        * how much space to leave at start of eager TID entries for
+        * protocol use, on each TID
+        */
+       /* instead of calculating it */
+       unsigned port_port;
+       /* chip offset of PIO buffers for this port */
+       u32 port_piobufs;
+       /* how many alloc_pages() chunks in port_rcvegrbuf_pages */
+       u32 port_rcvegrbuf_chunks;
+       /* how many egrbufs per chunk */
+       u32 port_rcvegrbufs_perchunk;
+       /* order for port_rcvegrbuf_pages */
+       size_t port_rcvegrbuf_size;
+       /* rcvhdrq size (for freeing) */
+       size_t port_rcvhdrq_size;
+       /* next expected TID to check when looking for free */
+       u32 port_tidcursor;
+       /* next expected TID to check */
+       unsigned long port_flag;
+       /* WAIT_RCV that timed out, no interrupt */
+       u32 port_rcvwait_to;
+       /* WAIT_PIO that timed out, no interrupt */
+       u32 port_piowait_to;
+       /* WAIT_RCV already happened, no wait */
+       u32 port_rcvnowait;
+       /* WAIT_PIO already happened, no wait */
+       u32 port_pionowait;
+       /* total number of rcvhdrqfull errors */
+       u32 port_hdrqfull;
+       /* pid of process using this port */
+       pid_t port_pid;
+       /* same size as task_struct .comm[] */
+       char port_comm[16];
+       /* pkeys set by this use of this port */
+       u16 port_pkeys[4];
+       /* so file ops can get at unit */
+       struct ipath_devdata *port_dd;
+};
+
+struct sk_buff;
+
+/*
+ * control information for layered drivers
+ */
+struct _ipath_layer {
+       void *l_arg;
+};
+
+/* Verbs layer interface */
+struct _verbs_layer {
+       void *l_arg;
+       struct timer_list l_timer;
+};
+
+struct ipath_devdata {
+       struct list_head ipath_list;
+
+       struct ipath_kregs const *ipath_kregs;
+       struct ipath_cregs const *ipath_cregs;
+
+       /* mem-mapped pointer to base of chip regs */
+       u64 __iomem *ipath_kregbase;
+       /* end of mem-mapped chip space; range checking */
+       u64 __iomem *ipath_kregend;
+       /* physical address of chip for io_remap, etc. */
+       unsigned long ipath_physaddr;
+       /* base of memory alloced for ipath_kregbase, for free */
+       u64 *ipath_kregalloc;
+       /*
+        * version of kregbase that doesn't have high bits set (for 32 bit
+        * programs, so mmap64 44 bit works)
+        */
+       u64 __iomem *ipath_kregvirt;
+       /*
+        * virtual address where port0 rcvhdrqtail updated for this unit.
+        * only written to by the chip, not the driver.
+        */
+       volatile __le64 *ipath_hdrqtailptr;
+       dma_addr_t ipath_dma_addr;
+       /* ipath_cfgports pointers */
+       struct ipath_portdata **ipath_pd;
+       /* sk_buffs used by port 0 eager receive queue */
+       struct sk_buff **ipath_port0_skbs;
+       /* kvirt address of 1st 2k pio buffer */
+       void __iomem *ipath_pio2kbase;
+       /* kvirt address of 1st 4k pio buffer */
+       void __iomem *ipath_pio4kbase;
+       /*
+        * points to area where PIOavail registers will be DMA'ed.
+        * Has to be on a page of it's own, because the page will be
+        * mapped into user program space.  This copy is *ONLY* ever
+        * written by DMA, not by the driver!  Need a copy per device
+        * when we get to multiple devices
+        */
+       volatile __le64 *ipath_pioavailregs_dma;
+       /* physical address where updates occur */
+       dma_addr_t ipath_pioavailregs_phys;
+       struct _ipath_layer ipath_layer;
+       /* setup intr */
+       int (*ipath_f_intrsetup)(struct ipath_devdata *);
+       /* setup on-chip bus config */
+       int (*ipath_f_bus)(struct ipath_devdata *, struct pci_dev *);
+       /* hard reset chip */
+       int (*ipath_f_reset)(struct ipath_devdata *);
+       int (*ipath_f_get_boardname)(struct ipath_devdata *, char *,
+                                    size_t);
+       void (*ipath_f_init_hwerrors)(struct ipath_devdata *);
+       void (*ipath_f_handle_hwerrors)(struct ipath_devdata *, char *,
+                                       size_t);
+       void (*ipath_f_quiet_serdes)(struct ipath_devdata *);
+       int (*ipath_f_bringup_serdes)(struct ipath_devdata *);
+       int (*ipath_f_early_init)(struct ipath_devdata *);
+       void (*ipath_f_clear_tids)(struct ipath_devdata *, unsigned);
+       void (*ipath_f_put_tid)(struct ipath_devdata *, u64 __iomem*,
+                               u32, unsigned long);
+       void (*ipath_f_tidtemplate)(struct ipath_devdata *);
+       void (*ipath_f_cleanup)(struct ipath_devdata *);
+       void (*ipath_f_setextled)(struct ipath_devdata *, u64, u64);
+       /* fill out chip-specific fields */
+       int (*ipath_f_get_base_info)(struct ipath_portdata *, void *);
+       struct _verbs_layer verbs_layer;
+       /* total dwords sent (summed from counter) */
+       u64 ipath_sword;
+       /* total dwords rcvd (summed from counter) */
+       u64 ipath_rword;
+       /* total packets sent (summed from counter) */
+       u64 ipath_spkts;
+       /* total packets rcvd (summed from counter) */
+       u64 ipath_rpkts;
+       /* ipath_statusp initially points to this. */
+       u64 _ipath_status;
+       /* GUID for this interface, in network order */
+       __be64 ipath_guid;
+       /*
+        * aggregrate of error bits reported since last cleared, for
+        * limiting of error reporting
+        */
+       ipath_err_t ipath_lasterror;
+       /*
+        * aggregrate of error bits reported since last cleared, for
+        * limiting of hwerror reporting
+        */
+       ipath_err_t ipath_lasthwerror;
+       /*
+        * errors masked because they occur too fast, also includes errors
+        * that are always ignored (ipath_ignorederrs)
+        */
+       ipath_err_t ipath_maskederrs;
+       /* time in jiffies at which to re-enable maskederrs */
+       unsigned long ipath_unmasktime;
+       /*
+        * errors always ignored (masked), at least for a given
+        * chip/device, because they are wrong or not useful
+        */
+       ipath_err_t ipath_ignorederrs;
+       /* count of egrfull errors, combined for all ports */
+       u64 ipath_last_tidfull;
+       /* for ipath_qcheck() */
+       u64 ipath_lastport0rcv_cnt;
+       /* template for writing TIDs  */
+       u64 ipath_tidtemplate;
+       /* value to write to free TIDs */
+       u64 ipath_tidinvalid;
+       /* PE-800 rcv interrupt setup */
+       u64 ipath_rhdrhead_intr_off;
+
+       /* size of memory at ipath_kregbase */
+       u32 ipath_kregsize;
+       /* number of registers used for pioavail */
+       u32 ipath_pioavregs;
+       /* IPATH_POLL, etc. */
+       u32 ipath_flags;
+       /* ipath_flags sma is waiting for */
+       u32 ipath_sma_state_wanted;
+       /* last buffer for user use, first buf for kernel use is this
+        * index. */
+       u32 ipath_lastport_piobuf;
+       /* is a stats timer active */
+       u32 ipath_stats_timer_active;
+       /* dwords sent read from counter */
+       u32 ipath_lastsword;
+       /* dwords received read from counter */
+       u32 ipath_lastrword;
+       /* sent packets read from counter */
+       u32 ipath_lastspkts;
+       /* received packets read from counter */
+       u32 ipath_lastrpkts;
+       /* pio bufs allocated per port */
+       u32 ipath_pbufsport;
+       /*
+        * number of ports configured as max; zero is set to number chip
+        * supports, less gives more pio bufs/port, etc.
+        */
+       u32 ipath_cfgports;
+       /* port0 rcvhdrq head offset */
+       u32 ipath_port0head;
+       /* count of port 0 hdrqfull errors */
+       u32 ipath_p0_hdrqfull;
+
+       /*
+        * (*cfgports) used to suppress multiple instances of same
+        * port staying stuck at same point
+        */
+       u32 *ipath_lastrcvhdrqtails;
+       /*
+        * (*cfgports) used to suppress multiple instances of same
+        * port staying stuck at same point
+        */
+       u32 *ipath_lastegrheads;
+       /*
+        * index of last piobuffer we used.  Speeds up searching, by
+        * starting at this point.  Doesn't matter if multiple cpu's use and
+        * update, last updater is only write that matters.  Whenever it
+        * wraps, we update shadow copies.  Need a copy per device when we
+        * get to multiple devices
+        */
+       u32 ipath_lastpioindex;
+       /* max length of freezemsg */
+       u32 ipath_freezelen;
+       /*
+        * consecutive times we wanted a PIO buffer but were unable to
+        * get one
+        */
+       u32 ipath_consec_nopiobuf;
+       /*
+        * hint that we should update ipath_pioavailshadow before
+        * looking for a PIO buffer
+        */
+       u32 ipath_upd_pio_shadow;
+       /* so we can rewrite it after a chip reset */
+       u32 ipath_pcibar0;
+       /* so we can rewrite it after a chip reset */
+       u32 ipath_pcibar1;
+       /* sequential tries for SMA send and no bufs */
+       u32 ipath_nosma_bufs;
+       /* duration (seconds) ipath_nosma_bufs set */
+       u32 ipath_nosma_secs;
+
+       /* HT/PCI Vendor ID (here for NodeInfo) */
+       u16 ipath_vendorid;
+       /* HT/PCI Device ID (here for NodeInfo) */
+       u16 ipath_deviceid;
+       /* offset in HT config space of slave/primary interface block */
+       u8 ipath_ht_slave_off;
+       /* for write combining settings */
+       unsigned long ipath_wc_cookie;
+       /* ref count for each pkey */
+       atomic_t ipath_pkeyrefs[4];
+       /* shadow copy of all exptids physaddr; used only by funcsim */
+       u64 *ipath_tidsimshadow;
+       /* shadow copy of struct page *'s for exp tid pages */
+       struct page **ipath_pageshadow;
+       /* lock to workaround chip bug 9437 */
+       spinlock_t ipath_tid_lock;
+
+       /*
+        * IPATH_STATUS_*,
+        * this address is mapped readonly into user processes so they can
+        * get status cheaply, whenever they want.
+        */
+       u64 *ipath_statusp;
+       /* freeze msg if hw error put chip in freeze */
+       char *ipath_freezemsg;
+       /* pci access data structure */
+       struct pci_dev *pcidev;
+       struct cdev *cdev;
+       struct class_device *class_dev;
+       /* timer used to prevent stats overflow, error throttling, etc. */
+       struct timer_list ipath_stats_timer;
+       /* check for stale messages in rcv queue */
+       /* only allow one intr at a time. */
+       unsigned long ipath_rcv_pending;
+
+       /*
+        * Shadow copies of registers; size indicates read access size.
+        * Most of them are readonly, but some are write-only register,
+        * where we manipulate the bits in the shadow copy, and then write
+        * the shadow copy to infinipath.
+        *
+        * We deliberately make most of these 32 bits, since they have
+        * restricted range.  For any that we read, we won't to generate 32
+        * bit accesses, since Opteron will generate 2 separate 32 bit HT
+        * transactions for a 64 bit read, and we want to avoid unnecessary
+        * HT transactions.
+        */
+
+       /* This is the 64 bit group */
+
+       /*
+        * shadow of pioavail, check to be sure it's large enough at
+        * init time.
+        */
+       unsigned long ipath_pioavailshadow[8];
+       /* shadow of kr_gpio_out, for rmw ops */
+       u64 ipath_gpio_out;
+       /* kr_revision shadow */
+       u64 ipath_revision;
+       /*
+        * shadow of ibcctrl, for interrupt handling of link changes,
+        * etc.
+        */
+       u64 ipath_ibcctrl;
+       /*
+        * last ibcstatus, to suppress "duplicate" status change messages,
+        * mostly from 2 to 3
+        */
+       u64 ipath_lastibcstat;
+       /* hwerrmask shadow */
+       ipath_err_t ipath_hwerrmask;
+       /* interrupt config reg shadow */
+       u64 ipath_intconfig;
+       /* kr_sendpiobufbase value */
+       u64 ipath_piobufbase;
+
+       /* these are the "32 bit" regs */
+
+       /*
+        * number of GUIDs in the flash for this interface; may need some
+        * rethinking for setting on other ifaces
+        */
+       u32 ipath_nguid;
+       /*
+        * the following two are 32-bit bitmasks, but {test,clear,set}_bit
+        * all expect bit fields to be "unsigned long"
+        */
+       /* shadow kr_rcvctrl */
+       unsigned long ipath_rcvctrl;
+       /* shadow kr_sendctrl */
+       unsigned long ipath_sendctrl;
+
+       /* value we put in kr_rcvhdrcnt */
+       u32 ipath_rcvhdrcnt;
+       /* value we put in kr_rcvhdrsize */
+       u32 ipath_rcvhdrsize;
+       /* value we put in kr_rcvhdrentsize */
+       u32 ipath_rcvhdrentsize;
+       /* offset of last entry in rcvhdrq */
+       u32 ipath_hdrqlast;
+       /* kr_portcnt value */
+       u32 ipath_portcnt;
+       /* kr_pagealign value */
+       u32 ipath_palign;
+       /* number of "2KB" PIO buffers */
+       u32 ipath_piobcnt2k;
+       /* size in bytes of "2KB" PIO buffers */
+       u32 ipath_piosize2k;
+       /* number of "4KB" PIO buffers */
+       u32 ipath_piobcnt4k;
+       /* size in bytes of "4KB" PIO buffers */
+       u32 ipath_piosize4k;
+       /* kr_rcvegrbase value */
+       u32 ipath_rcvegrbase;
+       /* kr_rcvegrcnt value */
+       u32 ipath_rcvegrcnt;
+       /* kr_rcvtidbase value */
+       u32 ipath_rcvtidbase;
+       /* kr_rcvtidcnt value */
+       u32 ipath_rcvtidcnt;
+       /* kr_sendregbase */
+       u32 ipath_sregbase;
+       /* kr_userregbase */
+       u32 ipath_uregbase;
+       /* kr_counterregbase */
+       u32 ipath_cregbase;
+       /* shadow the control register contents */
+       u32 ipath_control;
+       /* shadow the gpio output contents */
+       u32 ipath_extctrl;
+       /* PCI revision register (HTC rev on FPGA) */
+       u32 ipath_pcirev;
+
+       /* chip address space used by 4k pio buffers */
+       u32 ipath_4kalign;
+       /* The MTU programmed for this unit */
+       u32 ipath_ibmtu;
+       /*
+        * The max size IB packet, included IB headers that we can send.
+        * Starts same as ipath_piosize, but is affected when ibmtu is
+        * changed, or by size of eager buffers
+        */
+       u32 ipath_ibmaxlen;
+       /*
+        * ibmaxlen at init time, limited by chip and by receive buffer
+        * size.  Not changed after init.
+        */
+       u32 ipath_init_ibmaxlen;
+       /* size of each rcvegrbuffer */
+       u32 ipath_rcvegrbufsize;
+       /* width (2,4,8,16,32) from HT config reg */
+       u32 ipath_htwidth;
+       /* HT speed (200,400,800,1000) from HT config */
+       u32 ipath_htspeed;
+       /* ports waiting for PIOavail intr */
+       unsigned long ipath_portpiowait;
+       /*
+        * number of sequential ibcstatus change for polling active/quiet
+        * (i.e., link not coming up).
+        */
+       u32 ipath_ibpollcnt;
+       /* low and high portions of MSI capability/vector */
+       u32 ipath_msi_lo;
+       /* saved after PCIe init for restore after reset */
+       u32 ipath_msi_hi;
+       /* MSI data (vector) saved for restore */
+       u16 ipath_msi_data;
+       /* MLID programmed for this instance */
+       u16 ipath_mlid;
+       /* LID programmed for this instance */
+       u16 ipath_lid;
+       /* list of pkeys programmed; 0 if not set */
+       u16 ipath_pkeys[4];
+       /* ASCII serial number, from flash */
+       u8 ipath_serial[12];
+       /* human readable board version */
+       u8 ipath_boardversion[80];
+       /* chip major rev, from ipath_revision */
+       u8 ipath_majrev;
+       /* chip minor rev, from ipath_revision */
+       u8 ipath_minrev;
+       /* board rev, from ipath_revision */
+       u8 ipath_boardrev;
+       /* unit # of this chip, if present */
+       int ipath_unit;
+       /* saved for restore after reset */
+       u8 ipath_pci_cacheline;
+       /* LID mask control */
+       u8 ipath_lmc;
+};
+
+extern volatile __le64 *ipath_port0_rcvhdrtail;
+extern dma_addr_t ipath_port0_rcvhdrtail_dma;
+
+#define IPATH_PORT0_RCVHDRTAIL_SIZE PAGE_SIZE
+
+extern struct list_head ipath_dev_list;
+extern spinlock_t ipath_devs_lock;
+extern struct ipath_devdata *ipath_lookup(int unit);
+
+extern u16 ipath_layer_rcv_opcode;
+extern int ipath_verbs_registered;
+extern int __ipath_layer_intr(struct ipath_devdata *, u32);
+extern int ipath_layer_intr(struct ipath_devdata *, u32);
+extern int __ipath_layer_rcv(struct ipath_devdata *, void *,
+                            struct sk_buff *);
+extern int __ipath_layer_rcv_lid(struct ipath_devdata *, void *);
+extern int __ipath_verbs_piobufavail(struct ipath_devdata *);
+extern int __ipath_verbs_rcv(struct ipath_devdata *, void *, void *, u32);
+
+void ipath_layer_add(struct ipath_devdata *);
+void ipath_layer_del(struct ipath_devdata *);
+
+int ipath_init_chip(struct ipath_devdata *, int);
+int ipath_enable_wc(struct ipath_devdata *dd);
+void ipath_disable_wc(struct ipath_devdata *dd);
+int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp);
+void ipath_shutdown_device(struct ipath_devdata *);
+
+struct file_operations;
+int ipath_cdev_init(int minor, char *name, struct file_operations *fops,
+                   struct cdev **cdevp, struct class_device **class_devp);
+void ipath_cdev_cleanup(struct cdev **cdevp,
+                       struct class_device **class_devp);
+
+int ipath_diag_init(void);
+void ipath_diag_cleanup(void);
+void ipath_diag_bringup_link(struct ipath_devdata *);
+
+extern wait_queue_head_t ipath_sma_state_wait;
+
+int ipath_user_add(struct ipath_devdata *dd);
+void ipath_user_del(struct ipath_devdata *dd);
+
+struct sk_buff *ipath_alloc_skb(struct ipath_devdata *dd, gfp_t);
+
+extern int ipath_diag_inuse;
+
+irqreturn_t ipath_intr(int irq, void *devid, struct pt_regs *regs);
+void ipath_decode_err(char *buf, size_t blen, ipath_err_t err);
+#if __IPATH_INFO || __IPATH_DBG
+extern const char *ipath_ibcstatus_str[];
+#endif
+
+/* clean up any per-chip chip-specific stuff */
+void ipath_chip_cleanup(struct ipath_devdata *);
+/* clean up any chip type-specific stuff */
+void ipath_chip_done(void);
+
+/* check to see if we have to force ordering for write combining */
+int ipath_unordered_wc(void);
+
+void ipath_disarm_piobufs(struct ipath_devdata *, unsigned first,
+                         unsigned cnt);
+
+int ipath_create_rcvhdrq(struct ipath_devdata *, struct ipath_portdata *);
+void ipath_free_pddata(struct ipath_devdata *, u32, int);
+
+int ipath_parse_ushort(const char *str, unsigned short *valp);
+
+int ipath_wait_linkstate(struct ipath_devdata *, u32, int);
+void ipath_set_ib_lstate(struct ipath_devdata *, int);
+void ipath_kreceive(struct ipath_devdata *);
+int ipath_setrcvhdrsize(struct ipath_devdata *, unsigned);
+int ipath_reset_device(int);
+void ipath_get_faststats(unsigned long);
+
+/* for use in system calls, where we want to know device type, etc. */
+#define port_fp(fp) ((struct ipath_portdata *) (fp)->private_data)
+
+/*
+ * values for ipath_flags
+ */
+/* The chip is up and initted */
+#define IPATH_INITTED       0x2
+               /* set if any user code has set kr_rcvhdrsize */
+#define IPATH_RCVHDRSZ_SET  0x4
+               /* The chip is present and valid for accesses */
+#define IPATH_PRESENT       0x8
+               /* HT link0 is only 8 bits wide, ignore upper byte crc
+                * errors, etc. */
+#define IPATH_8BIT_IN_HT0   0x10
+               /* HT link1 is only 8 bits wide, ignore upper byte crc
+                * errors, etc. */
+#define IPATH_8BIT_IN_HT1   0x20
+               /* The link is down */
+#define IPATH_LINKDOWN      0x40
+               /* The link level is up (0x11) */
+#define IPATH_LINKINIT      0x80
+               /* The link is in the armed (0x21) state */
+#define IPATH_LINKARMED     0x100
+               /* The link is in the active (0x31) state */
+#define IPATH_LINKACTIVE    0x200
+               /* link current state is unknown */
+#define IPATH_LINKUNK       0x400
+               /* no IB cable, or no device on IB cable */
+#define IPATH_NOCABLE       0x4000
+               /* Supports port zero per packet receive interrupts via
+                * GPIO */
+#define IPATH_GPIO_INTR     0x8000
+               /* uses the coded 4byte TID, not 8 byte */
+#define IPATH_4BYTE_TID     0x10000
+               /* packet/word counters are 32 bit, else those 4 counters
+                * are 64bit */
+#define IPATH_32BITCOUNTERS 0x20000
+               /* can miss port0 rx interrupts */
+#define IPATH_POLL_RX_INTR  0x40000
+#define IPATH_DISABLED      0x80000 /* administratively disabled */
+
+/* portdata flag bit offsets */
+               /* waiting for a packet to arrive */
+#define IPATH_PORT_WAITING_RCV   2
+               /* waiting for a PIO buffer to be available */
+#define IPATH_PORT_WAITING_PIO   3
+
+/* free up any allocated data at closes */
+void ipath_free_data(struct ipath_portdata *dd);
+int ipath_waitfor_mdio_cmdready(struct ipath_devdata *);
+int ipath_waitfor_complete(struct ipath_devdata *, ipath_kreg, u64, u64 *);
+u32 __iomem *ipath_getpiobuf(struct ipath_devdata *, u32 *);
+/* init PE-800-specific func */
+void ipath_init_pe800_funcs(struct ipath_devdata *);
+/* init HT-400-specific func */
+void ipath_init_ht400_funcs(struct ipath_devdata *);
+void ipath_get_guid(struct ipath_devdata *);
+u64 ipath_snap_cntr(struct ipath_devdata *, ipath_creg);
+
+/*
+ * number of words used for protocol header if not set by ipath_userinit();
+ */
+#define IPATH_DFLT_RCVHDRSIZE 9
+
+#define IPATH_MDIO_CMD_WRITE   1
+#define IPATH_MDIO_CMD_READ    2
+#define IPATH_MDIO_CLD_DIV     25      /* to get 2.5 Mhz mdio clock */
+#define IPATH_MDIO_CMDVALID    0x40000000      /* bit 30 */
+#define IPATH_MDIO_DATAVALID   0x80000000      /* bit 31 */
+#define IPATH_MDIO_CTRL_STD    0x0
+
+static inline u64 ipath_mdio_req(int cmd, int dev, int reg, int data)
+{
+       return (((u64) IPATH_MDIO_CLD_DIV) << 32) |
+               (cmd << 26) |
+               (dev << 21) |
+               (reg << 16) |
+               (data & 0xFFFF);
+}
+
+               /* signal and fifo status, in bank 31 */
+#define IPATH_MDIO_CTRL_XGXS_REG_8  0x8
+               /* controls loopback, redundancy */
+#define IPATH_MDIO_CTRL_8355_REG_1  0x10
+               /* premph, encdec, etc. */
+#define IPATH_MDIO_CTRL_8355_REG_2  0x11
+               /* Kchars, etc. */
+#define IPATH_MDIO_CTRL_8355_REG_6  0x15
+#define IPATH_MDIO_CTRL_8355_REG_9  0x18
+#define IPATH_MDIO_CTRL_8355_REG_10 0x1D
+
+int ipath_get_user_pages(unsigned long, size_t, struct page **);
+int ipath_get_user_pages_nocopy(unsigned long, struct page **);
+void ipath_release_user_pages(struct page **, size_t);
+void ipath_release_user_pages_on_close(struct page **, size_t);
+int ipath_eeprom_read(struct ipath_devdata *, u8, void *, int);
+int ipath_eeprom_write(struct ipath_devdata *, u8, const void *, int);
+
+/* these are used for the registers that vary with port */
+void ipath_write_kreg_port(const struct ipath_devdata *, ipath_kreg,
+                          unsigned, u64);
+u64 ipath_read_kreg64_port(const struct ipath_devdata *, ipath_kreg,
+                          unsigned);
+
+/*
+ * We could have a single register get/put routine, that takes a group type,
+ * but this is somewhat clearer and cleaner.  It also gives us some error
+ * checking.  64 bit register reads should always work, but are inefficient
+ * on opteron (the northbridge always generates 2 separate HT 32 bit reads),
+ * so we use kreg32 wherever possible.  User register and counter register
+ * reads are always 32 bit reads, so only one form of those routines.
+ */
+
+/*
+ * At the moment, none of the s-registers are writable, so no
+ * ipath_write_sreg(), and none of the c-registers are writable, so no
+ * ipath_write_creg().
+ */
+
+/**
+ * ipath_read_ureg32 - read 32-bit virtualized per-port register
+ * @dd: device
+ * @regno: register number
+ * @port: port number
+ *
+ * Return the contents of a register that is virtualized to be per port.
+ * Prints a debug message and returns -1 on errors (not distinguishable from
+ * valid contents at runtime; we may add a separate error variable at some
+ * point).
+ *
+ * This is normally not used by the kernel, but may be for debugging, and
+ * has a different implementation than user mode, which is why it's not in
+ * _common.h.
+ */
+static inline u32 ipath_read_ureg32(const struct ipath_devdata *dd,
+                                   ipath_ureg regno, int port)
+{
+       if (!dd->ipath_kregbase)
+               return 0;
+
+       return readl(regno + (u64 __iomem *)
+                    (dd->ipath_uregbase +
+                     (char __iomem *)dd->ipath_kregbase +
+                     dd->ipath_palign * port));
+}
+
+/**
+ * ipath_write_ureg - write 32-bit virtualized per-port register
+ * @dd: device
+ * @regno: register number
+ * @value: value
+ * @port: port
+ *
+ * Write the contents of a register that is virtualized to be per port.
+ */
+static inline void ipath_write_ureg(const struct ipath_devdata *dd,
+                                   ipath_ureg regno, u64 value, int port)
+{
+       u64 __iomem *ubase = (u64 __iomem *)
+               (dd->ipath_uregbase + (char __iomem *) dd->ipath_kregbase +
+                dd->ipath_palign * port);
+       if (dd->ipath_kregbase)
+               writeq(value, &ubase[regno]);
+}
+
+static inline u32 ipath_read_kreg32(const struct ipath_devdata *dd,
+                                   ipath_kreg regno)
+{
+       if (!dd->ipath_kregbase)
+               return -1;
+       return readl((u32 __iomem *) & dd->ipath_kregbase[regno]);
+}
+
+static inline u64 ipath_read_kreg64(const struct ipath_devdata *dd,
+                                   ipath_kreg regno)
+{
+       if (!dd->ipath_kregbase)
+               return -1;
+
+       return readq(&dd->ipath_kregbase[regno]);
+}
+
+static inline void ipath_write_kreg(const struct ipath_devdata *dd,
+                                   ipath_kreg regno, u64 value)
+{
+       if (dd->ipath_kregbase)
+               writeq(value, &dd->ipath_kregbase[regno]);
+}
+
+static inline u64 ipath_read_creg(const struct ipath_devdata *dd,
+                                 ipath_sreg regno)
+{
+       if (!dd->ipath_kregbase)
+               return 0;
+
+       return readq(regno + (u64 __iomem *)
+                    (dd->ipath_cregbase +
+                     (char __iomem *)dd->ipath_kregbase));
+}
+
+static inline u32 ipath_read_creg32(const struct ipath_devdata *dd,
+                                        ipath_sreg regno)
+{
+       if (!dd->ipath_kregbase)
+               return 0;
+       return readl(regno + (u64 __iomem *)
+                    (dd->ipath_cregbase +
+                     (char __iomem *)dd->ipath_kregbase));
+}
+
+/*
+ * sysfs interface.
+ */
+
+struct device_driver;
+
+extern const char ipath_core_version[];
+
+int ipath_driver_create_group(struct device_driver *);
+void ipath_driver_remove_group(struct device_driver *);
+
+int ipath_device_create_group(struct device *, struct ipath_devdata *);
+void ipath_device_remove_group(struct device *, struct ipath_devdata *);
+int ipath_expose_reset(struct device *);
+
+int ipath_init_ipathfs(void);
+void ipath_exit_ipathfs(void);
+int ipathfs_add_device(struct ipath_devdata *);
+int ipathfs_remove_device(struct ipath_devdata *);
+
+/*
+ * Flush write combining store buffers (if present) and perform a write
+ * barrier.
+ */
+#if defined(CONFIG_X86_64)
+#define ipath_flush_wc() asm volatile("sfence" ::: "memory")
+#else
+#define ipath_flush_wc() wmb()
+#endif
+
+extern unsigned ipath_debug; /* debugging bit mask */
+
+const char *ipath_get_unit_name(int unit);
+
+extern struct mutex ipath_mutex;
+
+#define IPATH_DRV_NAME         "ipath_core"
+#define IPATH_MAJOR            233
+#define IPATH_SMA_MINOR                128
+#define IPATH_DIAG_MINOR       129
+#define IPATH_NMINORS          130
+
+#define ipath_dev_err(dd,fmt,...) \
+       do { \
+               const struct ipath_devdata *__dd = (dd); \
+               if (__dd->pcidev) \
+                       dev_err(&__dd->pcidev->dev, "%s: " fmt, \
+                               ipath_get_unit_name(__dd->ipath_unit), \
+                               ##__VA_ARGS__); \
+               else \
+                       printk(KERN_ERR IPATH_DRV_NAME ": %s: " fmt, \
+                              ipath_get_unit_name(__dd->ipath_unit), \
+                              ##__VA_ARGS__); \
+       } while (0)
+
+#if _IPATH_DEBUGGING
+
+# define __IPATH_DBG_WHICH(which,fmt,...) \
+       do { \
+               if(unlikely(ipath_debug&(which))) \
+                       printk(KERN_DEBUG IPATH_DRV_NAME ": %s: " fmt, \
+                              __func__,##__VA_ARGS__); \
+       } while(0)
+
+# define ipath_dbg(fmt,...) \
+       __IPATH_DBG_WHICH(__IPATH_DBG,fmt,##__VA_ARGS__)
+# define ipath_cdbg(which,fmt,...) \
+       __IPATH_DBG_WHICH(__IPATH_##which##DBG,fmt,##__VA_ARGS__)
+
+#else /* ! _IPATH_DEBUGGING */
+
+# define ipath_dbg(fmt,...)
+# define ipath_cdbg(which,fmt,...)
+
+#endif /* _IPATH_DEBUGGING */
+
+#endif                         /* _IPATH_KERNEL_H */
diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c
new file mode 100644 (file)
index 0000000..aa33b0e
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <asm/io.h>
+
+#include "ipath_verbs.h"
+
+/**
+ * ipath_alloc_lkey - allocate an lkey
+ * @rkt: lkey table in which to allocate the lkey
+ * @mr: memory region that this lkey protects
+ *
+ * Returns 1 if successful, otherwise returns 0.
+ */
+
+int ipath_alloc_lkey(struct ipath_lkey_table *rkt, struct ipath_mregion *mr)
+{
+       unsigned long flags;
+       u32 r;
+       u32 n;
+       int ret;
+
+       spin_lock_irqsave(&rkt->lock, flags);
+
+       /* Find the next available LKEY */
+       r = n = rkt->next;
+       for (;;) {
+               if (rkt->table[r] == NULL)
+                       break;
+               r = (r + 1) & (rkt->max - 1);
+               if (r == n) {
+                       spin_unlock_irqrestore(&rkt->lock, flags);
+                       _VERBS_INFO("LKEY table full\n");
+                       ret = 0;
+                       goto bail;
+               }
+       }
+       rkt->next = (r + 1) & (rkt->max - 1);
+       /*
+        * Make sure lkey is never zero which is reserved to indicate an
+        * unrestricted LKEY.
+        */
+       rkt->gen++;
+       mr->lkey = (r << (32 - ib_ipath_lkey_table_size)) |
+               ((((1 << (24 - ib_ipath_lkey_table_size)) - 1) & rkt->gen)
+                << 8);
+       if (mr->lkey == 0) {
+               mr->lkey |= 1 << 8;
+               rkt->gen++;
+       }
+       rkt->table[r] = mr;
+       spin_unlock_irqrestore(&rkt->lock, flags);
+
+       ret = 1;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_free_lkey - free an lkey
+ * @rkt: table from which to free the lkey
+ * @lkey: lkey id to free
+ */
+void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey)
+{
+       unsigned long flags;
+       u32 r;
+
+       if (lkey == 0)
+               return;
+       r = lkey >> (32 - ib_ipath_lkey_table_size);
+       spin_lock_irqsave(&rkt->lock, flags);
+       rkt->table[r] = NULL;
+       spin_unlock_irqrestore(&rkt->lock, flags);
+}
+
+/**
+ * ipath_lkey_ok - check IB SGE for validity and initialize
+ * @rkt: table containing lkey to check SGE against
+ * @isge: outgoing internal SGE
+ * @sge: SGE to check
+ * @acc: access flags
+ *
+ * Return 1 if valid and successful, otherwise returns 0.
+ *
+ * Check the IB SGE for validity and initialize our internal version
+ * of it.
+ */
+int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
+                 struct ib_sge *sge, int acc)
+{
+       struct ipath_mregion *mr;
+       size_t off;
+       int ret;
+
+       /*
+        * We use LKEY == zero to mean a physical kmalloc() address.
+        * This is a bit of a hack since we rely on dma_map_single()
+        * being reversible by calling bus_to_virt().
+        */
+       if (sge->lkey == 0) {
+               isge->mr = NULL;
+               isge->vaddr = bus_to_virt(sge->addr);
+               isge->length = sge->length;
+               isge->sge_length = sge->length;
+               ret = 1;
+               goto bail;
+       }
+       spin_lock(&rkt->lock);
+       mr = rkt->table[(sge->lkey >> (32 - ib_ipath_lkey_table_size))];
+       spin_unlock(&rkt->lock);
+       if (unlikely(mr == NULL || mr->lkey != sge->lkey)) {
+               ret = 0;
+               goto bail;
+       }
+
+       off = sge->addr - mr->user_base;
+       if (unlikely(sge->addr < mr->user_base ||
+                    off + sge->length > mr->length ||
+                    (mr->access_flags & acc) != acc)) {
+               ret = 0;
+               goto bail;
+       }
+
+       off += mr->offset;
+       isge->mr = mr;
+       isge->m = 0;
+       isge->n = 0;
+       while (off >= mr->map[isge->m]->segs[isge->n].length) {
+               off -= mr->map[isge->m]->segs[isge->n].length;
+               isge->n++;
+               if (isge->n >= IPATH_SEGSZ) {
+                       isge->m++;
+                       isge->n = 0;
+               }
+       }
+       isge->vaddr = mr->map[isge->m]->segs[isge->n].vaddr + off;
+       isge->length = mr->map[isge->m]->segs[isge->n].length - off;
+       isge->sge_length = sge->length;
+
+       ret = 1;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_rkey_ok - check the IB virtual address, length, and RKEY
+ * @dev: infiniband device
+ * @ss: SGE state
+ * @len: length of data
+ * @vaddr: virtual address to place data
+ * @rkey: rkey to check
+ * @acc: access flags
+ *
+ * Return 1 if successful, otherwise 0.
+ *
+ * The QP r_rq.lock should be held.
+ */
+int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
+                 u32 len, u64 vaddr, u32 rkey, int acc)
+{
+       struct ipath_lkey_table *rkt = &dev->lk_table;
+       struct ipath_sge *sge = &ss->sge;
+       struct ipath_mregion *mr;
+       size_t off;
+       int ret;
+
+       spin_lock(&rkt->lock);
+       mr = rkt->table[(rkey >> (32 - ib_ipath_lkey_table_size))];
+       spin_unlock(&rkt->lock);
+       if (unlikely(mr == NULL || mr->lkey != rkey)) {
+               ret = 0;
+               goto bail;
+       }
+
+       off = vaddr - mr->iova;
+       if (unlikely(vaddr < mr->iova || off + len > mr->length ||
+                    (mr->access_flags & acc) == 0)) {
+               ret = 0;
+               goto bail;
+       }
+
+       off += mr->offset;
+       sge->mr = mr;
+       sge->m = 0;
+       sge->n = 0;
+       while (off >= mr->map[sge->m]->segs[sge->n].length) {
+               off -= mr->map[sge->m]->segs[sge->n].length;
+               sge->n++;
+               if (sge->n >= IPATH_SEGSZ) {
+                       sge->m++;
+                       sge->n = 0;
+               }
+       }
+       sge->vaddr = mr->map[sge->m]->segs[sge->n].vaddr + off;
+       sge->length = mr->map[sge->m]->segs[sge->n].length - off;
+       sge->sge_length = len;
+       ss->sg_list = NULL;
+       ss->num_sge = 1;
+
+       ret = 1;
+
+bail:
+       return ret;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_layer.c b/drivers/infiniband/hw/ipath/ipath_layer.c
new file mode 100644 (file)
index 0000000..2cabf63
--- /dev/null
@@ -0,0 +1,1515 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * These are the routines used by layered drivers, currently just the
+ * layered ethernet driver and verbs layer.
+ */
+
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <asm/byteorder.h>
+
+#include "ipath_kernel.h"
+#include "ips_common.h"
+#include "ipath_layer.h"
+
+/* Acquire before ipath_devs_lock. */
+static DEFINE_MUTEX(ipath_layer_mutex);
+
+u16 ipath_layer_rcv_opcode;
+static int (*layer_intr)(void *, u32);
+static int (*layer_rcv)(void *, void *, struct sk_buff *);
+static int (*layer_rcv_lid)(void *, void *);
+static int (*verbs_piobufavail)(void *);
+static void (*verbs_rcv)(void *, void *, void *, u32);
+int ipath_verbs_registered;
+
+static void *(*layer_add_one)(int, struct ipath_devdata *);
+static void (*layer_remove_one)(void *);
+static void *(*verbs_add_one)(int, struct ipath_devdata *);
+static void (*verbs_remove_one)(void *);
+static void (*verbs_timer_cb)(void *);
+
+int __ipath_layer_intr(struct ipath_devdata *dd, u32 arg)
+{
+       int ret = -ENODEV;
+
+       if (dd->ipath_layer.l_arg && layer_intr)
+               ret = layer_intr(dd->ipath_layer.l_arg, arg);
+
+       return ret;
+}
+
+int ipath_layer_intr(struct ipath_devdata *dd, u32 arg)
+{
+       int ret;
+
+       mutex_lock(&ipath_layer_mutex);
+
+       ret = __ipath_layer_intr(dd, arg);
+
+       mutex_unlock(&ipath_layer_mutex);
+
+       return ret;
+}
+
+int __ipath_layer_rcv(struct ipath_devdata *dd, void *hdr,
+                     struct sk_buff *skb)
+{
+       int ret = -ENODEV;
+
+       if (dd->ipath_layer.l_arg && layer_rcv)
+               ret = layer_rcv(dd->ipath_layer.l_arg, hdr, skb);
+
+       return ret;
+}
+
+int __ipath_layer_rcv_lid(struct ipath_devdata *dd, void *hdr)
+{
+       int ret = -ENODEV;
+
+       if (dd->ipath_layer.l_arg && layer_rcv_lid)
+               ret = layer_rcv_lid(dd->ipath_layer.l_arg, hdr);
+
+       return ret;
+}
+
+int __ipath_verbs_piobufavail(struct ipath_devdata *dd)
+{
+       int ret = -ENODEV;
+
+       if (dd->verbs_layer.l_arg && verbs_piobufavail)
+               ret = verbs_piobufavail(dd->verbs_layer.l_arg);
+
+       return ret;
+}
+
+int __ipath_verbs_rcv(struct ipath_devdata *dd, void *rc, void *ebuf,
+                     u32 tlen)
+{
+       int ret = -ENODEV;
+
+       if (dd->verbs_layer.l_arg && verbs_rcv) {
+               verbs_rcv(dd->verbs_layer.l_arg, rc, ebuf, tlen);
+               ret = 0;
+       }
+
+       return ret;
+}
+
+int ipath_layer_set_linkstate(struct ipath_devdata *dd, u8 newstate)
+{
+       u32 lstate;
+       int ret;
+
+       switch (newstate) {
+       case IPATH_IB_LINKDOWN:
+               ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKINITCMD_POLL <<
+                                   INFINIPATH_IBCC_LINKINITCMD_SHIFT);
+               /* don't wait */
+               ret = 0;
+               goto bail;
+
+       case IPATH_IB_LINKDOWN_SLEEP:
+               ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKINITCMD_SLEEP <<
+                                   INFINIPATH_IBCC_LINKINITCMD_SHIFT);
+               /* don't wait */
+               ret = 0;
+               goto bail;
+
+       case IPATH_IB_LINKDOWN_DISABLE:
+               ipath_set_ib_lstate(dd,
+                                   INFINIPATH_IBCC_LINKINITCMD_DISABLE <<
+                                   INFINIPATH_IBCC_LINKINITCMD_SHIFT);
+               /* don't wait */
+               ret = 0;
+               goto bail;
+
+       case IPATH_IB_LINKINIT:
+               if (dd->ipath_flags & IPATH_LINKINIT) {
+                       ret = 0;
+                       goto bail;
+               }
+               ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKCMD_INIT <<
+                                   INFINIPATH_IBCC_LINKCMD_SHIFT);
+               lstate = IPATH_LINKINIT;
+               break;
+
+       case IPATH_IB_LINKARM:
+               if (dd->ipath_flags & IPATH_LINKARMED) {
+                       ret = 0;
+                       goto bail;
+               }
+               if (!(dd->ipath_flags &
+                     (IPATH_LINKINIT | IPATH_LINKACTIVE))) {
+                       ret = -EINVAL;
+                       goto bail;
+               }
+               ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKCMD_ARMED <<
+                                   INFINIPATH_IBCC_LINKCMD_SHIFT);
+               /*
+                * Since the port can transition to ACTIVE by receiving
+                * a non VL 15 packet, wait for either state.
+                */
+               lstate = IPATH_LINKARMED | IPATH_LINKACTIVE;
+               break;
+
+       case IPATH_IB_LINKACTIVE:
+               if (dd->ipath_flags & IPATH_LINKACTIVE) {
+                       ret = 0;
+                       goto bail;
+               }
+               if (!(dd->ipath_flags & IPATH_LINKARMED)) {
+                       ret = -EINVAL;
+                       goto bail;
+               }
+               ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKCMD_ACTIVE <<
+                                   INFINIPATH_IBCC_LINKCMD_SHIFT);
+               lstate = IPATH_LINKACTIVE;
+               break;
+
+       default:
+               ipath_dbg("Invalid linkstate 0x%x requested\n", newstate);
+               ret = -EINVAL;
+               goto bail;
+       }
+       ret = ipath_wait_linkstate(dd, lstate, 2000);
+
+bail:
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_set_linkstate);
+
+/**
+ * ipath_layer_set_mtu - set the MTU
+ * @dd: the infinipath device
+ * @arg: the new MTU
+ *
+ * we can handle "any" incoming size, the issue here is whether we
+ * need to restrict our outgoing size.   For now, we don't do any
+ * sanity checking on this, and we don't deal with what happens to
+ * programs that are already running when the size changes.
+ * NOTE: changing the MTU will usually cause the IBC to go back to
+ * link initialize (IPATH_IBSTATE_INIT) state...
+ */
+int ipath_layer_set_mtu(struct ipath_devdata *dd, u16 arg)
+{
+       u32 piosize;
+       int changed = 0;
+       int ret;
+
+       /*
+        * mtu is IB data payload max.  It's the largest power of 2 less
+        * than piosize (or even larger, since it only really controls the
+        * largest we can receive; we can send the max of the mtu and
+        * piosize).  We check that it's one of the valid IB sizes.
+        */
+       if (arg != 256 && arg != 512 && arg != 1024 && arg != 2048 &&
+           arg != 4096) {
+               ipath_dbg("Trying to set invalid mtu %u, failing\n", arg);
+               ret = -EINVAL;
+               goto bail;
+       }
+       if (dd->ipath_ibmtu == arg) {
+               ret = 0;        /* same as current */
+               goto bail;
+       }
+
+       piosize = dd->ipath_ibmaxlen;
+       dd->ipath_ibmtu = arg;
+
+       if (arg >= (piosize - IPATH_PIO_MAXIBHDR)) {
+               /* Only if it's not the initial value (or reset to it) */
+               if (piosize != dd->ipath_init_ibmaxlen) {
+                       dd->ipath_ibmaxlen = piosize;
+                       changed = 1;
+               }
+       } else if ((arg + IPATH_PIO_MAXIBHDR) != dd->ipath_ibmaxlen) {
+               piosize = arg + IPATH_PIO_MAXIBHDR;
+               ipath_cdbg(VERBOSE, "ibmaxlen was 0x%x, setting to 0x%x "
+                          "(mtu 0x%x)\n", dd->ipath_ibmaxlen, piosize,
+                          arg);
+               dd->ipath_ibmaxlen = piosize;
+               changed = 1;
+       }
+
+       if (changed) {
+               /*
+                * set the IBC maxpktlength to the size of our pio
+                * buffers in words
+                */
+               u64 ibc = dd->ipath_ibcctrl;
+               ibc &= ~(INFINIPATH_IBCC_MAXPKTLEN_MASK <<
+                        INFINIPATH_IBCC_MAXPKTLEN_SHIFT);
+
+               piosize = piosize - 2 * sizeof(u32);    /* ignore pbc */
+               dd->ipath_ibmaxlen = piosize;
+               piosize /= sizeof(u32); /* in words */
+               /*
+                * for ICRC, which we only send in diag test pkt mode, and
+                * we don't need to worry about that for mtu
+                */
+               piosize += 1;
+
+               ibc |= piosize << INFINIPATH_IBCC_MAXPKTLEN_SHIFT;
+               dd->ipath_ibcctrl = ibc;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl,
+                                dd->ipath_ibcctrl);
+               dd->ipath_f_tidtemplate(dd);
+       }
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_set_mtu);
+
+int ipath_set_sps_lid(struct ipath_devdata *dd, u32 arg, u8 lmc)
+{
+       ipath_stats.sps_lid[dd->ipath_unit] = arg;
+       dd->ipath_lid = arg;
+       dd->ipath_lmc = lmc;
+
+       mutex_lock(&ipath_layer_mutex);
+
+       if (dd->ipath_layer.l_arg && layer_intr)
+               layer_intr(dd->ipath_layer.l_arg, IPATH_LAYER_INT_LID);
+
+       mutex_unlock(&ipath_layer_mutex);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_set_sps_lid);
+
+int ipath_layer_set_guid(struct ipath_devdata *dd, __be64 guid)
+{
+       /* XXX - need to inform anyone who cares this just happened. */
+       dd->ipath_guid = guid;
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_set_guid);
+
+__be64 ipath_layer_get_guid(struct ipath_devdata *dd)
+{
+       return dd->ipath_guid;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_guid);
+
+u32 ipath_layer_get_nguid(struct ipath_devdata *dd)
+{
+       return dd->ipath_nguid;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_nguid);
+
+int ipath_layer_query_device(struct ipath_devdata *dd, u32 * vendor,
+                            u32 * boardrev, u32 * majrev, u32 * minrev)
+{
+       *vendor = dd->ipath_vendorid;
+       *boardrev = dd->ipath_boardrev;
+       *majrev = dd->ipath_majrev;
+       *minrev = dd->ipath_minrev;
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_query_device);
+
+u32 ipath_layer_get_flags(struct ipath_devdata *dd)
+{
+       return dd->ipath_flags;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_flags);
+
+struct device *ipath_layer_get_device(struct ipath_devdata *dd)
+{
+       return &dd->pcidev->dev;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_device);
+
+u16 ipath_layer_get_deviceid(struct ipath_devdata *dd)
+{
+       return dd->ipath_deviceid;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_deviceid);
+
+u64 ipath_layer_get_lastibcstat(struct ipath_devdata *dd)
+{
+       return dd->ipath_lastibcstat;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_lastibcstat);
+
+u32 ipath_layer_get_ibmtu(struct ipath_devdata *dd)
+{
+       return dd->ipath_ibmtu;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_ibmtu);
+
+void ipath_layer_add(struct ipath_devdata *dd)
+{
+       mutex_lock(&ipath_layer_mutex);
+
+       if (layer_add_one)
+               dd->ipath_layer.l_arg =
+                       layer_add_one(dd->ipath_unit, dd);
+
+       if (verbs_add_one)
+               dd->verbs_layer.l_arg =
+                       verbs_add_one(dd->ipath_unit, dd);
+
+       mutex_unlock(&ipath_layer_mutex);
+}
+
+void ipath_layer_del(struct ipath_devdata *dd)
+{
+       mutex_lock(&ipath_layer_mutex);
+
+       if (dd->ipath_layer.l_arg && layer_remove_one) {
+               layer_remove_one(dd->ipath_layer.l_arg);
+               dd->ipath_layer.l_arg = NULL;
+       }
+
+       if (dd->verbs_layer.l_arg && verbs_remove_one) {
+               verbs_remove_one(dd->verbs_layer.l_arg);
+               dd->verbs_layer.l_arg = NULL;
+       }
+
+       mutex_unlock(&ipath_layer_mutex);
+}
+
+int ipath_layer_register(void *(*l_add)(int, struct ipath_devdata *),
+                        void (*l_remove)(void *),
+                        int (*l_intr)(void *, u32),
+                        int (*l_rcv)(void *, void *, struct sk_buff *),
+                        u16 l_rcv_opcode,
+                        int (*l_rcv_lid)(void *, void *))
+{
+       struct ipath_devdata *dd, *tmp;
+       unsigned long flags;
+
+       mutex_lock(&ipath_layer_mutex);
+
+       layer_add_one = l_add;
+       layer_remove_one = l_remove;
+       layer_intr = l_intr;
+       layer_rcv = l_rcv;
+       layer_rcv_lid = l_rcv_lid;
+       ipath_layer_rcv_opcode = l_rcv_opcode;
+
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+
+       list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) {
+               if (!(dd->ipath_flags & IPATH_INITTED))
+                       continue;
+
+               if (dd->ipath_layer.l_arg)
+                       continue;
+
+               if (!(*dd->ipath_statusp & IPATH_STATUS_SMA))
+                       *dd->ipath_statusp |= IPATH_STATUS_OIB_SMA;
+
+               spin_unlock_irqrestore(&ipath_devs_lock, flags);
+               dd->ipath_layer.l_arg = l_add(dd->ipath_unit, dd);
+               spin_lock_irqsave(&ipath_devs_lock, flags);
+       }
+
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+       mutex_unlock(&ipath_layer_mutex);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_register);
+
+void ipath_layer_unregister(void)
+{
+       struct ipath_devdata *dd, *tmp;
+       unsigned long flags;
+
+       mutex_lock(&ipath_layer_mutex);
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+
+       list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) {
+               if (dd->ipath_layer.l_arg && layer_remove_one) {
+                       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+                       layer_remove_one(dd->ipath_layer.l_arg);
+                       spin_lock_irqsave(&ipath_devs_lock, flags);
+                       dd->ipath_layer.l_arg = NULL;
+               }
+       }
+
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+
+       layer_add_one = NULL;
+       layer_remove_one = NULL;
+       layer_intr = NULL;
+       layer_rcv = NULL;
+       layer_rcv_lid = NULL;
+
+       mutex_unlock(&ipath_layer_mutex);
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_unregister);
+
+static void __ipath_verbs_timer(unsigned long arg)
+{
+       struct ipath_devdata *dd = (struct ipath_devdata *) arg;
+
+       /*
+        * If port 0 receive packet interrupts are not available, or
+        * can be missed, poll the receive queue
+        */
+       if (dd->ipath_flags & IPATH_POLL_RX_INTR)
+               ipath_kreceive(dd);
+
+       /* Handle verbs layer timeouts. */
+       if (dd->verbs_layer.l_arg && verbs_timer_cb)
+               verbs_timer_cb(dd->verbs_layer.l_arg);
+
+       mod_timer(&dd->verbs_layer.l_timer, jiffies + 1);
+}
+
+/**
+ * ipath_verbs_register - verbs layer registration
+ * @l_piobufavail: callback for when PIO buffers become available
+ * @l_rcv: callback for receiving a packet
+ * @l_timer_cb: timer callback
+ * @ipath_devdata: device data structure is put here
+ */
+int ipath_verbs_register(void *(*l_add)(int, struct ipath_devdata *),
+                        void (*l_remove)(void *arg),
+                        int (*l_piobufavail) (void *arg),
+                        void (*l_rcv) (void *arg, void *rhdr,
+                                       void *data, u32 tlen),
+                        void (*l_timer_cb) (void *arg))
+{
+       struct ipath_devdata *dd, *tmp;
+       unsigned long flags;
+
+       mutex_lock(&ipath_layer_mutex);
+
+       verbs_add_one = l_add;
+       verbs_remove_one = l_remove;
+       verbs_piobufavail = l_piobufavail;
+       verbs_rcv = l_rcv;
+       verbs_timer_cb = l_timer_cb;
+
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+
+       list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) {
+               if (!(dd->ipath_flags & IPATH_INITTED))
+                       continue;
+
+               if (dd->verbs_layer.l_arg)
+                       continue;
+
+               spin_unlock_irqrestore(&ipath_devs_lock, flags);
+               dd->verbs_layer.l_arg = l_add(dd->ipath_unit, dd);
+               spin_lock_irqsave(&ipath_devs_lock, flags);
+       }
+
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+       mutex_unlock(&ipath_layer_mutex);
+
+       ipath_verbs_registered = 1;
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_verbs_register);
+
+void ipath_verbs_unregister(void)
+{
+       struct ipath_devdata *dd, *tmp;
+       unsigned long flags;
+
+       mutex_lock(&ipath_layer_mutex);
+       spin_lock_irqsave(&ipath_devs_lock, flags);
+
+       list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) {
+               *dd->ipath_statusp &= ~IPATH_STATUS_OIB_SMA;
+
+               if (dd->verbs_layer.l_arg && verbs_remove_one) {
+                       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+                       verbs_remove_one(dd->verbs_layer.l_arg);
+                       spin_lock_irqsave(&ipath_devs_lock, flags);
+                       dd->verbs_layer.l_arg = NULL;
+               }
+       }
+
+       spin_unlock_irqrestore(&ipath_devs_lock, flags);
+
+       verbs_add_one = NULL;
+       verbs_remove_one = NULL;
+       verbs_piobufavail = NULL;
+       verbs_rcv = NULL;
+       verbs_timer_cb = NULL;
+
+       mutex_unlock(&ipath_layer_mutex);
+}
+
+EXPORT_SYMBOL_GPL(ipath_verbs_unregister);
+
+int ipath_layer_open(struct ipath_devdata *dd, u32 * pktmax)
+{
+       int ret;
+       u32 intval = 0;
+
+       mutex_lock(&ipath_layer_mutex);
+
+       if (!dd->ipath_layer.l_arg) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       ret = ipath_setrcvhdrsize(dd, NUM_OF_EXTRA_WORDS_IN_HEADER_QUEUE);
+
+       if (ret < 0)
+               goto bail;
+
+       *pktmax = dd->ipath_ibmaxlen;
+
+       if (*dd->ipath_statusp & IPATH_STATUS_IB_READY)
+               intval |= IPATH_LAYER_INT_IF_UP;
+       if (ipath_stats.sps_lid[dd->ipath_unit])
+               intval |= IPATH_LAYER_INT_LID;
+       if (ipath_stats.sps_mlid[dd->ipath_unit])
+               intval |= IPATH_LAYER_INT_BCAST;
+       /*
+        * do this on open, in case low level is already up and
+        * just layered driver was reloaded, etc.
+        */
+       if (intval)
+               layer_intr(dd->ipath_layer.l_arg, intval);
+
+       ret = 0;
+bail:
+       mutex_unlock(&ipath_layer_mutex);
+
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_open);
+
+u16 ipath_layer_get_lid(struct ipath_devdata *dd)
+{
+       return dd->ipath_lid;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_lid);
+
+/**
+ * ipath_layer_get_mac - get the MAC address
+ * @dd: the infinipath device
+ * @mac: the MAC is put here
+ *
+ * This is the EUID-64 OUI octets (top 3), then
+ * skip the next 2 (which should both be zero or 0xff).
+ * The returned MAC is in network order
+ * mac points to at least 6 bytes of buffer
+ * We assume that by the time the LID is set, that the GUID is as valid
+ * as it's ever going to be, rather than adding yet another status bit.
+ */
+
+int ipath_layer_get_mac(struct ipath_devdata *dd, u8 * mac)
+{
+       u8 *guid;
+
+       guid = (u8 *) &dd->ipath_guid;
+
+       mac[0] = guid[0];
+       mac[1] = guid[1];
+       mac[2] = guid[2];
+       mac[3] = guid[5];
+       mac[4] = guid[6];
+       mac[5] = guid[7];
+       if ((guid[3] || guid[4]) && !(guid[3] == 0xff && guid[4] == 0xff))
+               ipath_dbg("Warning, guid bytes 3 and 4 not 0 or 0xffff: "
+                         "%x %x\n", guid[3], guid[4]);
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_mac);
+
+u16 ipath_layer_get_bcast(struct ipath_devdata *dd)
+{
+       return dd->ipath_mlid;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_bcast);
+
+u32 ipath_layer_get_cr_errpkey(struct ipath_devdata *dd)
+{
+       return ipath_read_creg32(dd, dd->ipath_cregs->cr_errpkey);
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_cr_errpkey);
+
+static void update_sge(struct ipath_sge_state *ss, u32 length)
+{
+       struct ipath_sge *sge = &ss->sge;
+
+       sge->vaddr += length;
+       sge->length -= length;
+       sge->sge_length -= length;
+       if (sge->sge_length == 0) {
+               if (--ss->num_sge)
+                       *sge = *ss->sg_list++;
+       } else if (sge->length == 0 && sge->mr != NULL) {
+               if (++sge->n >= IPATH_SEGSZ) {
+                       if (++sge->m >= sge->mr->mapsz)
+                               return;
+                       sge->n = 0;
+               }
+               sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr;
+               sge->length = sge->mr->map[sge->m]->segs[sge->n].length;
+       }
+}
+
+#ifdef __LITTLE_ENDIAN
+static inline u32 get_upper_bits(u32 data, u32 shift)
+{
+       return data >> shift;
+}
+
+static inline u32 set_upper_bits(u32 data, u32 shift)
+{
+       return data << shift;
+}
+
+static inline u32 clear_upper_bytes(u32 data, u32 n, u32 off)
+{
+       data <<= ((sizeof(u32) - n) * BITS_PER_BYTE);
+       data >>= ((sizeof(u32) - n - off) * BITS_PER_BYTE);
+       return data;
+}
+#else
+static inline u32 get_upper_bits(u32 data, u32 shift)
+{
+       return data << shift;
+}
+
+static inline u32 set_upper_bits(u32 data, u32 shift)
+{
+       return data >> shift;
+}
+
+static inline u32 clear_upper_bytes(u32 data, u32 n, u32 off)
+{
+       data >>= ((sizeof(u32) - n) * BITS_PER_BYTE);
+       data <<= ((sizeof(u32) - n - off) * BITS_PER_BYTE);
+       return data;
+}
+#endif
+
+static void copy_io(u32 __iomem *piobuf, struct ipath_sge_state *ss,
+                   u32 length)
+{
+       u32 extra = 0;
+       u32 data = 0;
+       u32 last;
+
+       while (1) {
+               u32 len = ss->sge.length;
+               u32 off;
+
+               BUG_ON(len == 0);
+               if (len > length)
+                       len = length;
+               if (len > ss->sge.sge_length)
+                       len = ss->sge.sge_length;
+               /* If the source address is not aligned, try to align it. */
+               off = (unsigned long)ss->sge.vaddr & (sizeof(u32) - 1);
+               if (off) {
+                       u32 *addr = (u32 *)((unsigned long)ss->sge.vaddr &
+                                           ~(sizeof(u32) - 1));
+                       u32 v = get_upper_bits(*addr, off * BITS_PER_BYTE);
+                       u32 y;
+
+                       y = sizeof(u32) - off;
+                       if (len > y)
+                               len = y;
+                       if (len + extra >= sizeof(u32)) {
+                               data |= set_upper_bits(v, extra *
+                                                      BITS_PER_BYTE);
+                               len = sizeof(u32) - extra;
+                               if (len == length) {
+                                       last = data;
+                                       break;
+                               }
+                               __raw_writel(data, piobuf);
+                               piobuf++;
+                               extra = 0;
+                               data = 0;
+                       } else {
+                               /* Clear unused upper bytes */
+                               data |= clear_upper_bytes(v, len, extra);
+                               if (len == length) {
+                                       last = data;
+                                       break;
+                               }
+                               extra += len;
+                       }
+               } else if (extra) {
+                       /* Source address is aligned. */
+                       u32 *addr = (u32 *) ss->sge.vaddr;
+                       int shift = extra * BITS_PER_BYTE;
+                       int ushift = 32 - shift;
+                       u32 l = len;
+
+                       while (l >= sizeof(u32)) {
+                               u32 v = *addr;
+
+                               data |= set_upper_bits(v, shift);
+                               __raw_writel(data, piobuf);
+                               data = get_upper_bits(v, ushift);
+                               piobuf++;
+                               addr++;
+                               l -= sizeof(u32);
+                       }
+                       /*
+                        * We still have 'extra' number of bytes leftover.
+                        */
+                       if (l) {
+                               u32 v = *addr;
+
+                               if (l + extra >= sizeof(u32)) {
+                                       data |= set_upper_bits(v, shift);
+                                       len -= l + extra - sizeof(u32);
+                                       if (len == length) {
+                                               last = data;
+                                               break;
+                                       }
+                                       __raw_writel(data, piobuf);
+                                       piobuf++;
+                                       extra = 0;
+                                       data = 0;
+                               } else {
+                                       /* Clear unused upper bytes */
+                                       data |= clear_upper_bytes(v, l,
+                                                                 extra);
+                                       if (len == length) {
+                                               last = data;
+                                               break;
+                                       }
+                                       extra += l;
+                               }
+                       } else if (len == length) {
+                               last = data;
+                               break;
+                       }
+               } else if (len == length) {
+                       u32 w;
+
+                       /*
+                        * Need to round up for the last dword in the
+                        * packet.
+                        */
+                       w = (len + 3) >> 2;
+                       __iowrite32_copy(piobuf, ss->sge.vaddr, w - 1);
+                       piobuf += w - 1;
+                       last = ((u32 *) ss->sge.vaddr)[w - 1];
+                       break;
+               } else {
+                       u32 w = len >> 2;
+
+                       __iowrite32_copy(piobuf, ss->sge.vaddr, w);
+                       piobuf += w;
+
+                       extra = len & (sizeof(u32) - 1);
+                       if (extra) {
+                               u32 v = ((u32 *) ss->sge.vaddr)[w];
+
+                               /* Clear unused upper bytes */
+                               data = clear_upper_bytes(v, extra, 0);
+                       }
+               }
+               update_sge(ss, len);
+               length -= len;
+       }
+       /* must flush early everything before trigger word */
+       ipath_flush_wc();
+       __raw_writel(last, piobuf);
+       /* be sure trigger word is written */
+       ipath_flush_wc();
+       update_sge(ss, length);
+}
+
+/**
+ * ipath_verbs_send - send a packet from the verbs layer
+ * @dd: the infinipath device
+ * @hdrwords: the number of works in the header
+ * @hdr: the packet header
+ * @len: the length of the packet in bytes
+ * @ss: the SGE to send
+ *
+ * This is like ipath_sma_send_pkt() in that we need to be able to send
+ * packets after the chip is initialized (MADs) but also like
+ * ipath_layer_send_hdr() since its used by the verbs layer.
+ */
+int ipath_verbs_send(struct ipath_devdata *dd, u32 hdrwords,
+                    u32 *hdr, u32 len, struct ipath_sge_state *ss)
+{
+       u32 __iomem *piobuf;
+       u32 plen;
+       int ret;
+
+       /* +1 is for the qword padding of pbc */
+       plen = hdrwords + ((len + 3) >> 2) + 1;
+       if (unlikely((plen << 2) > dd->ipath_ibmaxlen)) {
+               ipath_dbg("packet len 0x%x too long, failing\n", plen);
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       /* Get a PIO buffer to use. */
+       piobuf = ipath_getpiobuf(dd, NULL);
+       if (unlikely(piobuf == NULL)) {
+               ret = -EBUSY;
+               goto bail;
+       }
+
+       /*
+        * Write len to control qword, no flags.
+        * We have to flush after the PBC for correctness on some cpus
+        * or WC buffer can be written out of order.
+        */
+       writeq(plen, piobuf);
+       ipath_flush_wc();
+       piobuf += 2;
+       if (len == 0) {
+               /*
+                * If there is just the header portion, must flush before
+                * writing last word of header for correctness, and after
+                * the last header word (trigger word).
+                */
+               __iowrite32_copy(piobuf, hdr, hdrwords - 1);
+               ipath_flush_wc();
+               __raw_writel(hdr[hdrwords - 1], piobuf + hdrwords - 1);
+               ipath_flush_wc();
+               ret = 0;
+               goto bail;
+       }
+
+       __iowrite32_copy(piobuf, hdr, hdrwords);
+       piobuf += hdrwords;
+
+       /* The common case is aligned and contained in one segment. */
+       if (likely(ss->num_sge == 1 && len <= ss->sge.length &&
+                  !((unsigned long)ss->sge.vaddr & (sizeof(u32) - 1)))) {
+               u32 w;
+
+               /* Need to round up for the last dword in the packet. */
+               w = (len + 3) >> 2;
+               __iowrite32_copy(piobuf, ss->sge.vaddr, w - 1);
+               /* must flush early everything before trigger word */
+               ipath_flush_wc();
+               __raw_writel(((u32 *) ss->sge.vaddr)[w - 1],
+                            piobuf + w - 1);
+               /* be sure trigger word is written */
+               ipath_flush_wc();
+               update_sge(ss, len);
+               ret = 0;
+               goto bail;
+       }
+       copy_io(piobuf, ss, len);
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ipath_verbs_send);
+
+int ipath_layer_snapshot_counters(struct ipath_devdata *dd, u64 *swords,
+                                 u64 *rwords, u64 *spkts, u64 *rpkts,
+                                 u64 *xmit_wait)
+{
+       int ret;
+
+       if (!(dd->ipath_flags & IPATH_INITTED)) {
+               /* no hardware, freeze, etc. */
+               ipath_dbg("unit %u not usable\n", dd->ipath_unit);
+               ret = -EINVAL;
+               goto bail;
+       }
+       *swords = ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordsendcnt);
+       *rwords = ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordrcvcnt);
+       *spkts = ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktsendcnt);
+       *rpkts = ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktrcvcnt);
+       *xmit_wait = ipath_snap_cntr(dd, dd->ipath_cregs->cr_sendstallcnt);
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_snapshot_counters);
+
+/**
+ * ipath_layer_get_counters - get various chip counters
+ * @dd: the infinipath device
+ * @cntrs: counters are placed here
+ *
+ * Return the counters needed by recv_pma_get_portcounters().
+ */
+int ipath_layer_get_counters(struct ipath_devdata *dd,
+                             struct ipath_layer_counters *cntrs)
+{
+       int ret;
+
+       if (!(dd->ipath_flags & IPATH_INITTED)) {
+               /* no hardware, freeze, etc. */
+               ipath_dbg("unit %u not usable\n", dd->ipath_unit);
+               ret = -EINVAL;
+               goto bail;
+       }
+       cntrs->symbol_error_counter =
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_ibsymbolerrcnt);
+       cntrs->link_error_recovery_counter =
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_iblinkerrrecovcnt);
+       cntrs->link_downed_counter =
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_iblinkdowncnt);
+       cntrs->port_rcv_errors =
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_rxdroppktcnt) +
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_rcvovflcnt) +
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_portovflcnt) +
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_errrcvflowctrlcnt) +
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_err_rlencnt) +
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_invalidrlencnt) +
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_erricrccnt) +
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_errvcrccnt) +
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_errlpcrccnt) +
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_errlinkcnt) +
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_badformatcnt);
+       cntrs->port_rcv_remphys_errors =
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_rcvebpcnt);
+       cntrs->port_xmit_discards =
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_unsupvlcnt);
+       cntrs->port_xmit_data =
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordsendcnt);
+       cntrs->port_rcv_data =
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordrcvcnt);
+       cntrs->port_xmit_packets =
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktsendcnt);
+       cntrs->port_rcv_packets =
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktrcvcnt);
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_counters);
+
+int ipath_layer_want_buffer(struct ipath_devdata *dd)
+{
+       set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                        dd->ipath_sendctrl);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_want_buffer);
+
+int ipath_layer_send_hdr(struct ipath_devdata *dd, struct ether_header *hdr)
+{
+       int ret = 0;
+       u32 __iomem *piobuf;
+       u32 plen, *uhdr;
+       size_t count;
+       __be16 vlsllnh;
+
+       if (!(dd->ipath_flags & IPATH_RCVHDRSZ_SET)) {
+               ipath_dbg("send while not open\n");
+               ret = -EINVAL;
+       } else
+               if ((dd->ipath_flags & (IPATH_LINKUNK | IPATH_LINKDOWN)) ||
+                   dd->ipath_lid == 0) {
+                       /*
+                        * lid check is for when sma hasn't yet configured
+                        */
+                       ret = -ENETDOWN;
+                       ipath_cdbg(VERBOSE, "send while not ready, "
+                                  "mylid=%u, flags=0x%x\n",
+                                  dd->ipath_lid, dd->ipath_flags);
+               }
+
+       vlsllnh = *((__be16 *) hdr);
+       if (vlsllnh != htons(IPS_LRH_BTH)) {
+               ipath_dbg("Warning: lrh[0] wrong (%x, not %x); "
+                         "not sending\n", be16_to_cpu(vlsllnh),
+                         IPS_LRH_BTH);
+               ret = -EINVAL;
+       }
+       if (ret)
+               goto done;
+
+       /* Get a PIO buffer to use. */
+       piobuf = ipath_getpiobuf(dd, NULL);
+       if (piobuf == NULL) {
+               ret = -EBUSY;
+               goto done;
+       }
+
+       plen = (sizeof(*hdr) >> 2); /* actual length */
+       ipath_cdbg(EPKT, "0x%x+1w pio %p\n", plen, piobuf);
+
+       writeq(plen+1, piobuf); /* len (+1 for pad) to pbc, no flags */
+       ipath_flush_wc();
+       piobuf += 2;
+       uhdr = (u32 *)hdr;
+       count = plen-1; /* amount we can copy before trigger word */
+       __iowrite32_copy(piobuf, uhdr, count);
+       ipath_flush_wc();
+       __raw_writel(uhdr[count], piobuf + count);
+       ipath_flush_wc(); /* ensure it's sent, now */
+
+       ipath_stats.sps_ether_spkts++;  /* ether packet sent */
+
+done:
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_send_hdr);
+
+int ipath_layer_set_piointbufavail_int(struct ipath_devdata *dd)
+{
+       set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);
+
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                        dd->ipath_sendctrl);
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_set_piointbufavail_int);
+
+int ipath_layer_enable_timer(struct ipath_devdata *dd)
+{
+       /*
+        * HT-400 has a design flaw where the chip and kernel idea
+        * of the tail register don't always agree, and therefore we won't
+        * get an interrupt on the next packet received.
+        * If the board supports per packet receive interrupts, use it.
+        * Otherwise, the timer function periodically checks for packets
+        * to cover this case.
+        * Either way, the timer is needed for verbs layer related
+        * processing.
+        */
+       if (dd->ipath_flags & IPATH_GPIO_INTR) {
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_debugportselect,
+                                0x2074076542310ULL);
+               /* Enable GPIO bit 2 interrupt */
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_mask,
+                                (u64) (1 << 2));
+       }
+
+       init_timer(&dd->verbs_layer.l_timer);
+       dd->verbs_layer.l_timer.function = __ipath_verbs_timer;
+       dd->verbs_layer.l_timer.data = (unsigned long)dd;
+       dd->verbs_layer.l_timer.expires = jiffies + 1;
+       add_timer(&dd->verbs_layer.l_timer);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_enable_timer);
+
+int ipath_layer_disable_timer(struct ipath_devdata *dd)
+{
+       /* Disable GPIO bit 2 interrupt */
+       if (dd->ipath_flags & IPATH_GPIO_INTR)
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_mask, 0);
+
+       del_timer_sync(&dd->verbs_layer.l_timer);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_disable_timer);
+
+/**
+ * ipath_layer_set_verbs_flags - set the verbs layer flags
+ * @dd: the infinipath device
+ * @flags: the flags to set
+ */
+int ipath_layer_set_verbs_flags(struct ipath_devdata *dd, unsigned flags)
+{
+       struct ipath_devdata *ss;
+       unsigned long lflags;
+
+       spin_lock_irqsave(&ipath_devs_lock, lflags);
+
+       list_for_each_entry(ss, &ipath_dev_list, ipath_list) {
+               if (!(ss->ipath_flags & IPATH_INITTED))
+                       continue;
+               if ((flags & IPATH_VERBS_KERNEL_SMA) &&
+                   !(*ss->ipath_statusp & IPATH_STATUS_SMA))
+                       *ss->ipath_statusp |= IPATH_STATUS_OIB_SMA;
+               else
+                       *ss->ipath_statusp &= ~IPATH_STATUS_OIB_SMA;
+       }
+
+       spin_unlock_irqrestore(&ipath_devs_lock, lflags);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_set_verbs_flags);
+
+/**
+ * ipath_layer_get_npkeys - return the size of the PKEY table for port 0
+ * @dd: the infinipath device
+ */
+unsigned ipath_layer_get_npkeys(struct ipath_devdata *dd)
+{
+       return ARRAY_SIZE(dd->ipath_pd[0]->port_pkeys);
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_npkeys);
+
+/**
+ * ipath_layer_get_pkey - return the indexed PKEY from the port 0 PKEY table
+ * @dd: the infinipath device
+ * @index: the PKEY index
+ */
+unsigned ipath_layer_get_pkey(struct ipath_devdata *dd, unsigned index)
+{
+       unsigned ret;
+
+       if (index >= ARRAY_SIZE(dd->ipath_pd[0]->port_pkeys))
+               ret = 0;
+       else
+               ret = dd->ipath_pd[0]->port_pkeys[index];
+
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_pkey);
+
+/**
+ * ipath_layer_get_pkeys - return the PKEY table for port 0
+ * @dd: the infinipath device
+ * @pkeys: the pkey table is placed here
+ */
+int ipath_layer_get_pkeys(struct ipath_devdata *dd, u16 * pkeys)
+{
+       struct ipath_portdata *pd = dd->ipath_pd[0];
+
+       memcpy(pkeys, pd->port_pkeys, sizeof(pd->port_pkeys));
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_pkeys);
+
+/**
+ * rm_pkey - decrecment the reference count for the given PKEY
+ * @dd: the infinipath device
+ * @key: the PKEY index
+ *
+ * Return true if this was the last reference and the hardware table entry
+ * needs to be changed.
+ */
+static int rm_pkey(struct ipath_devdata *dd, u16 key)
+{
+       int i;
+       int ret;
+
+       for (i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) {
+               if (dd->ipath_pkeys[i] != key)
+                       continue;
+               if (atomic_dec_and_test(&dd->ipath_pkeyrefs[i])) {
+                       dd->ipath_pkeys[i] = 0;
+                       ret = 1;
+                       goto bail;
+               }
+               break;
+       }
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+/**
+ * add_pkey - add the given PKEY to the hardware table
+ * @dd: the infinipath device
+ * @key: the PKEY
+ *
+ * Return an error code if unable to add the entry, zero if no change,
+ * or 1 if the hardware PKEY register needs to be updated.
+ */
+static int add_pkey(struct ipath_devdata *dd, u16 key)
+{
+       int i;
+       u16 lkey = key & 0x7FFF;
+       int any = 0;
+       int ret;
+
+       if (lkey == 0x7FFF) {
+               ret = 0;
+               goto bail;
+       }
+
+       /* Look for an empty slot or a matching PKEY. */
+       for (i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) {
+               if (!dd->ipath_pkeys[i]) {
+                       any++;
+                       continue;
+               }
+               /* If it matches exactly, try to increment the ref count */
+               if (dd->ipath_pkeys[i] == key) {
+                       if (atomic_inc_return(&dd->ipath_pkeyrefs[i]) > 1) {
+                               ret = 0;
+                               goto bail;
+                       }
+                       /* Lost the race. Look for an empty slot below. */
+                       atomic_dec(&dd->ipath_pkeyrefs[i]);
+                       any++;
+               }
+               /*
+                * It makes no sense to have both the limited and unlimited
+                * PKEY set at the same time since the unlimited one will
+                * disable the limited one.
+                */
+               if ((dd->ipath_pkeys[i] & 0x7FFF) == lkey) {
+                       ret = -EEXIST;
+                       goto bail;
+               }
+       }
+       if (!any) {
+               ret = -EBUSY;
+               goto bail;
+       }
+       for (i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) {
+               if (!dd->ipath_pkeys[i] &&
+                   atomic_inc_return(&dd->ipath_pkeyrefs[i]) == 1) {
+                       /* for ipathstats, etc. */
+                       ipath_stats.sps_pkeys[i] = lkey;
+                       dd->ipath_pkeys[i] = key;
+                       ret = 1;
+                       goto bail;
+               }
+       }
+       ret = -EBUSY;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_layer_set_pkeys - set the PKEY table for port 0
+ * @dd: the infinipath device
+ * @pkeys: the PKEY table
+ */
+int ipath_layer_set_pkeys(struct ipath_devdata *dd, u16 * pkeys)
+{
+       struct ipath_portdata *pd;
+       int i;
+       int changed = 0;
+
+       pd = dd->ipath_pd[0];
+
+       for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) {
+               u16 key = pkeys[i];
+               u16 okey = pd->port_pkeys[i];
+
+               if (key == okey)
+                       continue;
+               /*
+                * The value of this PKEY table entry is changing.
+                * Remove the old entry in the hardware's array of PKEYs.
+                */
+               if (okey & 0x7FFF)
+                       changed |= rm_pkey(dd, okey);
+               if (key & 0x7FFF) {
+                       int ret = add_pkey(dd, key);
+
+                       if (ret < 0)
+                               key = 0;
+                       else
+                               changed |= ret;
+               }
+               pd->port_pkeys[i] = key;
+       }
+       if (changed) {
+               u64 pkey;
+
+               pkey = (u64) dd->ipath_pkeys[0] |
+                       ((u64) dd->ipath_pkeys[1] << 16) |
+                       ((u64) dd->ipath_pkeys[2] << 32) |
+                       ((u64) dd->ipath_pkeys[3] << 48);
+               ipath_cdbg(VERBOSE, "p0 new pkey reg %llx\n",
+                          (unsigned long long) pkey);
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_partitionkey,
+                                pkey);
+       }
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_set_pkeys);
+
+/**
+ * ipath_layer_get_linkdowndefaultstate - get the default linkdown state
+ * @dd: the infinipath device
+ *
+ * Returns zero if the default is POLL, 1 if the default is SLEEP.
+ */
+int ipath_layer_get_linkdowndefaultstate(struct ipath_devdata *dd)
+{
+       return !!(dd->ipath_ibcctrl & INFINIPATH_IBCC_LINKDOWNDEFAULTSTATE);
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_linkdowndefaultstate);
+
+/**
+ * ipath_layer_set_linkdowndefaultstate - set the default linkdown state
+ * @dd: the infinipath device
+ * @sleep: the new state
+ *
+ * Note that this will only take effect when the link state changes.
+ */
+int ipath_layer_set_linkdowndefaultstate(struct ipath_devdata *dd,
+                                        int sleep)
+{
+       if (sleep)
+               dd->ipath_ibcctrl |= INFINIPATH_IBCC_LINKDOWNDEFAULTSTATE;
+       else
+               dd->ipath_ibcctrl &= ~INFINIPATH_IBCC_LINKDOWNDEFAULTSTATE;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl,
+                        dd->ipath_ibcctrl);
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_set_linkdowndefaultstate);
+
+int ipath_layer_get_phyerrthreshold(struct ipath_devdata *dd)
+{
+       return (dd->ipath_ibcctrl >>
+               INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT) &
+               INFINIPATH_IBCC_PHYERRTHRESHOLD_MASK;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_phyerrthreshold);
+
+/**
+ * ipath_layer_set_phyerrthreshold - set the physical error threshold
+ * @dd: the infinipath device
+ * @n: the new threshold
+ *
+ * Note that this will only take effect when the link state changes.
+ */
+int ipath_layer_set_phyerrthreshold(struct ipath_devdata *dd, unsigned n)
+{
+       unsigned v;
+
+       v = (dd->ipath_ibcctrl >> INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT) &
+               INFINIPATH_IBCC_PHYERRTHRESHOLD_MASK;
+       if (v != n) {
+               dd->ipath_ibcctrl &=
+                       ~(INFINIPATH_IBCC_PHYERRTHRESHOLD_MASK <<
+                         INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT);
+               dd->ipath_ibcctrl |=
+                       (u64) n << INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl,
+                                dd->ipath_ibcctrl);
+       }
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_set_phyerrthreshold);
+
+int ipath_layer_get_overrunthreshold(struct ipath_devdata *dd)
+{
+       return (dd->ipath_ibcctrl >>
+               INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT) &
+               INFINIPATH_IBCC_OVERRUNTHRESHOLD_MASK;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_get_overrunthreshold);
+
+/**
+ * ipath_layer_set_overrunthreshold - set the overrun threshold
+ * @dd: the infinipath device
+ * @n: the new threshold
+ *
+ * Note that this will only take effect when the link state changes.
+ */
+int ipath_layer_set_overrunthreshold(struct ipath_devdata *dd, unsigned n)
+{
+       unsigned v;
+
+       v = (dd->ipath_ibcctrl >> INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT) &
+               INFINIPATH_IBCC_OVERRUNTHRESHOLD_MASK;
+       if (v != n) {
+               dd->ipath_ibcctrl &=
+                       ~(INFINIPATH_IBCC_OVERRUNTHRESHOLD_MASK <<
+                         INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT);
+               dd->ipath_ibcctrl |=
+                       (u64) n << INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl,
+                                dd->ipath_ibcctrl);
+       }
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipath_layer_set_overrunthreshold);
+
+int ipath_layer_get_boardname(struct ipath_devdata *dd, char *name,
+                             size_t namelen)
+{
+       return dd->ipath_f_get_boardname(dd, name, namelen);
+}
+EXPORT_SYMBOL_GPL(ipath_layer_get_boardname);
+
+u32 ipath_layer_get_rcvhdrentsize(struct ipath_devdata *dd)
+{
+       return dd->ipath_rcvhdrentsize;
+}
+EXPORT_SYMBOL_GPL(ipath_layer_get_rcvhdrentsize);
diff --git a/drivers/infiniband/hw/ipath/ipath_layer.h b/drivers/infiniband/hw/ipath/ipath_layer.h
new file mode 100644 (file)
index 0000000..6fefd15
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _IPATH_LAYER_H
+#define _IPATH_LAYER_H
+
+/*
+ * This header file is for symbols shared between the infinipath driver
+ * and drivers layered upon it (such as ipath).
+ */
+
+struct sk_buff;
+struct ipath_sge_state;
+struct ipath_devdata;
+struct ether_header;
+
+struct ipath_layer_counters {
+       u64 symbol_error_counter;
+       u64 link_error_recovery_counter;
+       u64 link_downed_counter;
+       u64 port_rcv_errors;
+       u64 port_rcv_remphys_errors;
+       u64 port_xmit_discards;
+       u64 port_xmit_data;
+       u64 port_rcv_data;
+       u64 port_xmit_packets;
+       u64 port_rcv_packets;
+};
+
+/*
+ * A segment is a linear region of low physical memory.
+ * XXX Maybe we should use phys addr here and kmap()/kunmap().
+ * Used by the verbs layer.
+ */
+struct ipath_seg {
+       void *vaddr;
+       size_t length;
+};
+
+/* The number of ipath_segs that fit in a page. */
+#define IPATH_SEGSZ     (PAGE_SIZE / sizeof (struct ipath_seg))
+
+struct ipath_segarray {
+       struct ipath_seg segs[IPATH_SEGSZ];
+};
+
+struct ipath_mregion {
+       u64 user_base;          /* User's address for this region */
+       u64 iova;               /* IB start address of this region */
+       size_t length;
+       u32 lkey;
+       u32 offset;             /* offset (bytes) to start of region */
+       int access_flags;
+       u32 max_segs;           /* number of ipath_segs in all the arrays */
+       u32 mapsz;              /* size of the map array */
+       struct ipath_segarray *map[0];  /* the segments */
+};
+
+/*
+ * These keep track of the copy progress within a memory region.
+ * Used by the verbs layer.
+ */
+struct ipath_sge {
+       struct ipath_mregion *mr;
+       void *vaddr;            /* current pointer into the segment */
+       u32 sge_length;         /* length of the SGE */
+       u32 length;             /* remaining length of the segment */
+       u16 m;                  /* current index: mr->map[m] */
+       u16 n;                  /* current index: mr->map[m]->segs[n] */
+};
+
+struct ipath_sge_state {
+       struct ipath_sge *sg_list;      /* next SGE to be used if any */
+       struct ipath_sge sge;   /* progress state for the current SGE */
+       u8 num_sge;
+};
+
+int ipath_layer_register(void *(*l_add)(int, struct ipath_devdata *),
+                        void (*l_remove)(void *),
+                        int (*l_intr)(void *, u32),
+                        int (*l_rcv)(void *, void *,
+                                     struct sk_buff *),
+                        u16 rcv_opcode,
+                        int (*l_rcv_lid)(void *, void *));
+int ipath_verbs_register(void *(*l_add)(int, struct ipath_devdata *),
+                        void (*l_remove)(void *arg),
+                        int (*l_piobufavail)(void *arg),
+                        void (*l_rcv)(void *arg, void *rhdr,
+                                      void *data, u32 tlen),
+                        void (*l_timer_cb)(void *arg));
+void ipath_layer_unregister(void);
+void ipath_verbs_unregister(void);
+int ipath_layer_open(struct ipath_devdata *, u32 * pktmax);
+u16 ipath_layer_get_lid(struct ipath_devdata *dd);
+int ipath_layer_get_mac(struct ipath_devdata *dd, u8 *);
+u16 ipath_layer_get_bcast(struct ipath_devdata *dd);
+u32 ipath_layer_get_cr_errpkey(struct ipath_devdata *dd);
+int ipath_layer_set_linkstate(struct ipath_devdata *dd, u8 state);
+int ipath_layer_set_mtu(struct ipath_devdata *, u16);
+int ipath_set_sps_lid(struct ipath_devdata *, u32, u8);
+int ipath_layer_send_hdr(struct ipath_devdata *dd,
+                        struct ether_header *hdr);
+int ipath_verbs_send(struct ipath_devdata *dd, u32 hdrwords,
+                    u32 * hdr, u32 len, struct ipath_sge_state *ss);
+int ipath_layer_set_piointbufavail_int(struct ipath_devdata *dd);
+int ipath_layer_get_boardname(struct ipath_devdata *dd, char *name,
+                             size_t namelen);
+int ipath_layer_snapshot_counters(struct ipath_devdata *dd, u64 *swords,
+                                 u64 *rwords, u64 *spkts, u64 *rpkts,
+                                 u64 *xmit_wait);
+int ipath_layer_get_counters(struct ipath_devdata *dd,
+                            struct ipath_layer_counters *cntrs);
+int ipath_layer_want_buffer(struct ipath_devdata *dd);
+int ipath_layer_set_guid(struct ipath_devdata *, __be64 guid);
+__be64 ipath_layer_get_guid(struct ipath_devdata *);
+u32 ipath_layer_get_nguid(struct ipath_devdata *);
+int ipath_layer_query_device(struct ipath_devdata *, u32 * vendor,
+                            u32 * boardrev, u32 * majrev, u32 * minrev);
+u32 ipath_layer_get_flags(struct ipath_devdata *dd);
+struct device *ipath_layer_get_device(struct ipath_devdata *dd);
+u16 ipath_layer_get_deviceid(struct ipath_devdata *dd);
+u64 ipath_layer_get_lastibcstat(struct ipath_devdata *dd);
+u32 ipath_layer_get_ibmtu(struct ipath_devdata *dd);
+int ipath_layer_enable_timer(struct ipath_devdata *dd);
+int ipath_layer_disable_timer(struct ipath_devdata *dd);
+int ipath_layer_set_verbs_flags(struct ipath_devdata *dd, unsigned flags);
+unsigned ipath_layer_get_npkeys(struct ipath_devdata *dd);
+unsigned ipath_layer_get_pkey(struct ipath_devdata *dd, unsigned index);
+int ipath_layer_get_pkeys(struct ipath_devdata *dd, u16 *pkeys);
+int ipath_layer_set_pkeys(struct ipath_devdata *dd, u16 *pkeys);
+int ipath_layer_get_linkdowndefaultstate(struct ipath_devdata *dd);
+int ipath_layer_set_linkdowndefaultstate(struct ipath_devdata *dd,
+                                        int sleep);
+int ipath_layer_get_phyerrthreshold(struct ipath_devdata *dd);
+int ipath_layer_set_phyerrthreshold(struct ipath_devdata *dd, unsigned n);
+int ipath_layer_get_overrunthreshold(struct ipath_devdata *dd);
+int ipath_layer_set_overrunthreshold(struct ipath_devdata *dd, unsigned n);
+u32 ipath_layer_get_rcvhdrentsize(struct ipath_devdata *dd);
+
+/* ipath_ether interrupt values */
+#define IPATH_LAYER_INT_IF_UP 0x2
+#define IPATH_LAYER_INT_IF_DOWN 0x4
+#define IPATH_LAYER_INT_LID 0x8
+#define IPATH_LAYER_INT_SEND_CONTINUE 0x10
+#define IPATH_LAYER_INT_BCAST 0x40
+
+/* _verbs_layer.l_flags */
+#define IPATH_VERBS_KERNEL_SMA 0x1
+
+extern unsigned ipath_debug; /* debugging bit mask */
+
+#endif                         /* _IPATH_LAYER_H */
diff --git a/drivers/infiniband/hw/ipath/ipath_mad.c b/drivers/infiniband/hw/ipath/ipath_mad.c
new file mode 100644 (file)
index 0000000..f7f8391
--- /dev/null
@@ -0,0 +1,1352 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <rdma/ib_smi.h>
+
+#include "ipath_kernel.h"
+#include "ipath_verbs.h"
+#include "ips_common.h"
+
+#define IB_SMP_UNSUP_VERSION   __constant_htons(0x0004)
+#define IB_SMP_UNSUP_METHOD    __constant_htons(0x0008)
+#define IB_SMP_UNSUP_METH_ATTR __constant_htons(0x000C)
+#define IB_SMP_INVALID_FIELD   __constant_htons(0x001C)
+
+static int reply(struct ib_smp *smp)
+{
+       /*
+        * The verbs framework will handle the directed/LID route
+        * packet changes.
+        */
+       smp->method = IB_MGMT_METHOD_GET_RESP;
+       if (smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+               smp->status |= IB_SMP_DIRECTION;
+       return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;
+}
+
+static int recv_subn_get_nodedescription(struct ib_smp *smp,
+                                        struct ib_device *ibdev)
+{
+       if (smp->attr_mod)
+               smp->status |= IB_SMP_INVALID_FIELD;
+
+       strncpy(smp->data, ibdev->node_desc, sizeof(smp->data));
+
+       return reply(smp);
+}
+
+struct nodeinfo {
+       u8 base_version;
+       u8 class_version;
+       u8 node_type;
+       u8 num_ports;
+       __be64 sys_guid;
+       __be64 node_guid;
+       __be64 port_guid;
+       __be16 partition_cap;
+       __be16 device_id;
+       __be32 revision;
+       u8 local_port_num;
+       u8 vendor_id[3];
+} __attribute__ ((packed));
+
+static int recv_subn_get_nodeinfo(struct ib_smp *smp,
+                                 struct ib_device *ibdev, u8 port)
+{
+       struct nodeinfo *nip = (struct nodeinfo *)&smp->data;
+       struct ipath_devdata *dd = to_idev(ibdev)->dd;
+       u32 vendor, boardid, majrev, minrev;
+
+       if (smp->attr_mod)
+               smp->status |= IB_SMP_INVALID_FIELD;
+
+       nip->base_version = 1;
+       nip->class_version = 1;
+       nip->node_type = 1;     /* channel adapter */
+       /*
+        * XXX The num_ports value will need a layer function to get
+        * the value if we ever have more than one IB port on a chip.
+        * We will also need to get the GUID for the port.
+        */
+       nip->num_ports = ibdev->phys_port_cnt;
+       /* This is already in network order */
+       nip->sys_guid = to_idev(ibdev)->sys_image_guid;
+       nip->node_guid = ipath_layer_get_guid(dd);
+       nip->port_guid = nip->sys_guid;
+       nip->partition_cap = cpu_to_be16(ipath_layer_get_npkeys(dd));
+       nip->device_id = cpu_to_be16(ipath_layer_get_deviceid(dd));
+       ipath_layer_query_device(dd, &vendor, &boardid, &majrev, &minrev);
+       nip->revision = cpu_to_be32((majrev << 16) | minrev);
+       nip->local_port_num = port;
+       nip->vendor_id[0] = 0;
+       nip->vendor_id[1] = vendor >> 8;
+       nip->vendor_id[2] = vendor;
+
+       return reply(smp);
+}
+
+static int recv_subn_get_guidinfo(struct ib_smp *smp,
+                                 struct ib_device *ibdev)
+{
+       u32 startgx = 8 * be32_to_cpu(smp->attr_mod);
+       __be64 *p = (__be64 *) smp->data;
+
+       /* 32 blocks of 8 64-bit GUIDs per block */
+
+       memset(smp->data, 0, sizeof(smp->data));
+
+       /*
+        * We only support one GUID for now.  If this changes, the
+        * portinfo.guid_cap field needs to be updated too.
+        */
+       if (startgx == 0)
+               /* The first is a copy of the read-only HW GUID. */
+               *p = ipath_layer_get_guid(to_idev(ibdev)->dd);
+       else
+               smp->status |= IB_SMP_INVALID_FIELD;
+
+       return reply(smp);
+}
+
+struct port_info {
+       __be64 mkey;
+       __be64 gid_prefix;
+       __be16 lid;
+       __be16 sm_lid;
+       __be32 cap_mask;
+       __be16 diag_code;
+       __be16 mkey_lease_period;
+       u8 local_port_num;
+       u8 link_width_enabled;
+       u8 link_width_supported;
+       u8 link_width_active;
+       u8 linkspeed_portstate;                 /* 4 bits, 4 bits */
+       u8 portphysstate_linkdown;              /* 4 bits, 4 bits */
+       u8 mkeyprot_resv_lmc;                   /* 2 bits, 3, 3 */
+       u8 linkspeedactive_enabled;             /* 4 bits, 4 bits */
+       u8 neighbormtu_mastersmsl;              /* 4 bits, 4 bits */
+       u8 vlcap_inittype;                      /* 4 bits, 4 bits */
+       u8 vl_high_limit;
+       u8 vl_arb_high_cap;
+       u8 vl_arb_low_cap;
+       u8 inittypereply_mtucap;                /* 4 bits, 4 bits */
+       u8 vlstallcnt_hoqlife;                  /* 3 bits, 5 bits */
+       u8 operationalvl_pei_peo_fpi_fpo;       /* 4 bits, 1, 1, 1, 1 */
+       __be16 mkey_violations;
+       __be16 pkey_violations;
+       __be16 qkey_violations;
+       u8 guid_cap;
+       u8 clientrereg_resv_subnetto;           /* 1 bit, 2 bits, 5 */
+       u8 resv_resptimevalue;                  /* 3 bits, 5 bits */
+       u8 localphyerrors_overrunerrors;        /* 4 bits, 4 bits */
+       __be16 max_credit_hint;
+       u8 resv;
+       u8 link_roundtrip_latency[3];
+} __attribute__ ((packed));
+
+static int recv_subn_get_portinfo(struct ib_smp *smp,
+                                 struct ib_device *ibdev, u8 port)
+{
+       struct ipath_ibdev *dev;
+       struct port_info *pip = (struct port_info *)smp->data;
+       u16 lid;
+       u8 ibcstat;
+       u8 mtu;
+       int ret;
+
+       if (be32_to_cpu(smp->attr_mod) > ibdev->phys_port_cnt) {
+               smp->status |= IB_SMP_INVALID_FIELD;
+               ret = reply(smp);
+               goto bail;
+       }
+
+       dev = to_idev(ibdev);
+
+       /* Clear all fields.  Only set the non-zero fields. */
+       memset(smp->data, 0, sizeof(smp->data));
+
+       /* Only return the mkey if the protection field allows it. */
+       if (smp->method == IB_MGMT_METHOD_SET || dev->mkey == smp->mkey ||
+           (dev->mkeyprot_resv_lmc >> 6) == 0)
+               pip->mkey = dev->mkey;
+       pip->gid_prefix = dev->gid_prefix;
+       lid = ipath_layer_get_lid(dev->dd);
+       pip->lid = lid ? cpu_to_be16(lid) : IB_LID_PERMISSIVE;
+       pip->sm_lid = cpu_to_be16(dev->sm_lid);
+       pip->cap_mask = cpu_to_be32(dev->port_cap_flags);
+       /* pip->diag_code; */
+       pip->mkey_lease_period = cpu_to_be16(dev->mkey_lease_period);
+       pip->local_port_num = port;
+       pip->link_width_enabled = dev->link_width_enabled;
+       pip->link_width_supported = 3;  /* 1x or 4x */
+       pip->link_width_active = 2;     /* 4x */
+       pip->linkspeed_portstate = 0x10;        /* 2.5Gbps */
+       ibcstat = ipath_layer_get_lastibcstat(dev->dd);
+       pip->linkspeed_portstate |= ((ibcstat >> 4) & 0x3) + 1;
+       pip->portphysstate_linkdown =
+               (ipath_cvt_physportstate[ibcstat & 0xf] << 4) |
+               (ipath_layer_get_linkdowndefaultstate(dev->dd) ? 1 : 2);
+       pip->mkeyprot_resv_lmc = dev->mkeyprot_resv_lmc;
+       pip->linkspeedactive_enabled = 0x11;    /* 2.5Gbps, 2.5Gbps */
+       switch (ipath_layer_get_ibmtu(dev->dd)) {
+       case 4096:
+               mtu = IB_MTU_4096;
+               break;
+       case 2048:
+               mtu = IB_MTU_2048;
+               break;
+       case 1024:
+               mtu = IB_MTU_1024;
+               break;
+       case 512:
+               mtu = IB_MTU_512;
+               break;
+       case 256:
+               mtu = IB_MTU_256;
+               break;
+       default:                /* oops, something is wrong */
+               mtu = IB_MTU_2048;
+               break;
+       }
+       pip->neighbormtu_mastersmsl = (mtu << 4) | dev->sm_sl;
+       pip->vlcap_inittype = 0x10;     /* VLCap = VL0, InitType = 0 */
+       pip->vl_high_limit = dev->vl_high_limit;
+       /* pip->vl_arb_high_cap; // only one VL */
+       /* pip->vl_arb_low_cap; // only one VL */
+       /* InitTypeReply = 0 */
+       pip->inittypereply_mtucap = IB_MTU_4096;
+       // HCAs ignore VLStallCount and HOQLife
+       /* pip->vlstallcnt_hoqlife; */
+       pip->operationalvl_pei_peo_fpi_fpo = 0x10;      /* OVLs = 1 */
+       pip->mkey_violations = cpu_to_be16(dev->mkey_violations);
+       /* P_KeyViolations are counted by hardware. */
+       pip->pkey_violations =
+               cpu_to_be16((ipath_layer_get_cr_errpkey(dev->dd) -
+                            dev->n_pkey_violations) & 0xFFFF);
+       pip->qkey_violations = cpu_to_be16(dev->qkey_violations);
+       /* Only the hardware GUID is supported for now */
+       pip->guid_cap = 1;
+       pip->clientrereg_resv_subnetto = dev->subnet_timeout;
+       /* 32.768 usec. response time (guessing) */
+       pip->resv_resptimevalue = 3;
+       pip->localphyerrors_overrunerrors =
+               (ipath_layer_get_phyerrthreshold(dev->dd) << 4) |
+               ipath_layer_get_overrunthreshold(dev->dd);
+       /* pip->max_credit_hint; */
+       /* pip->link_roundtrip_latency[3]; */
+
+       ret = reply(smp);
+
+bail:
+       return ret;
+}
+
+static int recv_subn_get_pkeytable(struct ib_smp *smp,
+                                  struct ib_device *ibdev)
+{
+       u32 startpx = 32 * (be32_to_cpu(smp->attr_mod) & 0xffff);
+       u16 *p = (u16 *) smp->data;
+       __be16 *q = (__be16 *) smp->data;
+
+       /* 64 blocks of 32 16-bit P_Key entries */
+
+       memset(smp->data, 0, sizeof(smp->data));
+       if (startpx == 0) {
+               struct ipath_ibdev *dev = to_idev(ibdev);
+               unsigned i, n = ipath_layer_get_npkeys(dev->dd);
+
+               ipath_layer_get_pkeys(dev->dd, p);
+
+               for (i = 0; i < n; i++)
+                       q[i] = cpu_to_be16(p[i]);
+       } else
+               smp->status |= IB_SMP_INVALID_FIELD;
+
+       return reply(smp);
+}
+
+static int recv_subn_set_guidinfo(struct ib_smp *smp,
+                                 struct ib_device *ibdev)
+{
+       /* The only GUID we support is the first read-only entry. */
+       return recv_subn_get_guidinfo(smp, ibdev);
+}
+
+/**
+ * recv_subn_set_portinfo - set port information
+ * @smp: the incoming SM packet
+ * @ibdev: the infiniband device
+ * @port: the port on the device
+ *
+ * Set Portinfo (see ch. 14.2.5.6).
+ */
+static int recv_subn_set_portinfo(struct ib_smp *smp,
+                                 struct ib_device *ibdev, u8 port)
+{
+       struct port_info *pip = (struct port_info *)smp->data;
+       struct ib_event event;
+       struct ipath_ibdev *dev;
+       u32 flags;
+       char clientrereg = 0;
+       u16 lid, smlid;
+       u8 lwe;
+       u8 lse;
+       u8 state;
+       u16 lstate;
+       u32 mtu;
+       int ret;
+
+       if (be32_to_cpu(smp->attr_mod) > ibdev->phys_port_cnt)
+               goto err;
+
+       dev = to_idev(ibdev);
+       event.device = ibdev;
+       event.element.port_num = port;
+
+       dev->mkey = pip->mkey;
+       dev->gid_prefix = pip->gid_prefix;
+       dev->mkey_lease_period = be16_to_cpu(pip->mkey_lease_period);
+
+       lid = be16_to_cpu(pip->lid);
+       if (lid != ipath_layer_get_lid(dev->dd)) {
+               /* Must be a valid unicast LID address. */
+               if (lid == 0 || lid >= IPS_MULTICAST_LID_BASE)
+                       goto err;
+               ipath_set_sps_lid(dev->dd, lid, pip->mkeyprot_resv_lmc & 7);
+               event.event = IB_EVENT_LID_CHANGE;
+               ib_dispatch_event(&event);
+       }
+
+       smlid = be16_to_cpu(pip->sm_lid);
+       if (smlid != dev->sm_lid) {
+               /* Must be a valid unicast LID address. */
+               if (smlid == 0 || smlid >= IPS_MULTICAST_LID_BASE)
+                       goto err;
+               dev->sm_lid = smlid;
+               event.event = IB_EVENT_SM_CHANGE;
+               ib_dispatch_event(&event);
+       }
+
+       /* Only 4x supported but allow 1x or 4x to be set (see 14.2.6.6). */
+       lwe = pip->link_width_enabled;
+       if ((lwe >= 4 && lwe <= 8) || (lwe >= 0xC && lwe <= 0xFE))
+               goto err;
+       if (lwe == 0xFF)
+               dev->link_width_enabled = 3;    /* 1x or 4x */
+       else if (lwe)
+               dev->link_width_enabled = lwe;
+
+       /* Only 2.5 Gbs supported. */
+       lse = pip->linkspeedactive_enabled & 0xF;
+       if (lse >= 2 && lse <= 0xE)
+               goto err;
+
+       /* Set link down default state. */
+       switch (pip->portphysstate_linkdown & 0xF) {
+       case 0: /* NOP */
+               break;
+       case 1: /* SLEEP */
+               if (ipath_layer_set_linkdowndefaultstate(dev->dd, 1))
+                       goto err;
+               break;
+       case 2: /* POLL */
+               if (ipath_layer_set_linkdowndefaultstate(dev->dd, 0))
+                       goto err;
+               break;
+       default:
+               goto err;
+       }
+
+       dev->mkeyprot_resv_lmc = pip->mkeyprot_resv_lmc;
+       dev->vl_high_limit = pip->vl_high_limit;
+
+       switch ((pip->neighbormtu_mastersmsl >> 4) & 0xF) {
+       case IB_MTU_256:
+               mtu = 256;
+               break;
+       case IB_MTU_512:
+               mtu = 512;
+               break;
+       case IB_MTU_1024:
+               mtu = 1024;
+               break;
+       case IB_MTU_2048:
+               mtu = 2048;
+               break;
+       case IB_MTU_4096:
+               mtu = 4096;
+               break;
+       default:
+               /* XXX We have already partially updated our state! */
+               goto err;
+       }
+       ipath_layer_set_mtu(dev->dd, mtu);
+
+       dev->sm_sl = pip->neighbormtu_mastersmsl & 0xF;
+
+       /* We only support VL0 */
+       if (((pip->operationalvl_pei_peo_fpi_fpo >> 4) & 0xF) > 1)
+               goto err;
+
+       if (pip->mkey_violations == 0)
+               dev->mkey_violations = 0;
+
+       /*
+        * Hardware counter can't be reset so snapshot and subtract
+        * later.
+        */
+       if (pip->pkey_violations == 0)
+               dev->n_pkey_violations =
+                       ipath_layer_get_cr_errpkey(dev->dd);
+
+       if (pip->qkey_violations == 0)
+               dev->qkey_violations = 0;
+
+       if (ipath_layer_set_phyerrthreshold(
+                   dev->dd,
+                   (pip->localphyerrors_overrunerrors >> 4) & 0xF))
+               goto err;
+
+       if (ipath_layer_set_overrunthreshold(
+                   dev->dd,
+                   (pip->localphyerrors_overrunerrors & 0xF)))
+               goto err;
+
+       dev->subnet_timeout = pip->clientrereg_resv_subnetto & 0x1F;
+
+       if (pip->clientrereg_resv_subnetto & 0x80) {
+               clientrereg = 1;
+               event.event = IB_EVENT_LID_CHANGE;
+               ib_dispatch_event(&event);
+       }
+
+       /*
+        * Do the port state change now that the other link parameters
+        * have been set.
+        * Changing the port physical state only makes sense if the link
+        * is down or is being set to down.
+        */
+       state = pip->linkspeed_portstate & 0xF;
+       flags = ipath_layer_get_flags(dev->dd);
+       lstate = (pip->portphysstate_linkdown >> 4) & 0xF;
+       if (lstate && !(state == IB_PORT_DOWN || state == IB_PORT_NOP))
+               goto err;
+
+       /*
+        * Only state changes of DOWN, ARM, and ACTIVE are valid
+        * and must be in the correct state to take effect (see 7.2.6).
+        */
+       switch (state) {
+       case IB_PORT_NOP:
+               if (lstate == 0)
+                       break;
+               /* FALLTHROUGH */
+       case IB_PORT_DOWN:
+               if (lstate == 0)
+                       if (ipath_layer_get_linkdowndefaultstate(dev->dd))
+                               lstate = IPATH_IB_LINKDOWN_SLEEP;
+                       else
+                               lstate = IPATH_IB_LINKDOWN;
+               else if (lstate == 1)
+                       lstate = IPATH_IB_LINKDOWN_SLEEP;
+               else if (lstate == 2)
+                       lstate = IPATH_IB_LINKDOWN;
+               else if (lstate == 3)
+                       lstate = IPATH_IB_LINKDOWN_DISABLE;
+               else
+                       goto err;
+               ipath_layer_set_linkstate(dev->dd, lstate);
+               if (flags & IPATH_LINKACTIVE) {
+                       event.event = IB_EVENT_PORT_ERR;
+                       ib_dispatch_event(&event);
+               }
+               break;
+       case IB_PORT_ARMED:
+               if (!(flags & (IPATH_LINKINIT | IPATH_LINKACTIVE)))
+                       break;
+               ipath_layer_set_linkstate(dev->dd, IPATH_IB_LINKARM);
+               if (flags & IPATH_LINKACTIVE) {
+                       event.event = IB_EVENT_PORT_ERR;
+                       ib_dispatch_event(&event);
+               }
+               break;
+       case IB_PORT_ACTIVE:
+               if (!(flags & IPATH_LINKARMED))
+                       break;
+               ipath_layer_set_linkstate(dev->dd, IPATH_IB_LINKACTIVE);
+               event.event = IB_EVENT_PORT_ACTIVE;
+               ib_dispatch_event(&event);
+               break;
+       default:
+               /* XXX We have already partially updated our state! */
+               goto err;
+       }
+
+       ret = recv_subn_get_portinfo(smp, ibdev, port);
+
+       if (clientrereg)
+               pip->clientrereg_resv_subnetto |= 0x80;
+
+       goto done;
+
+err:
+       smp->status |= IB_SMP_INVALID_FIELD;
+       ret = recv_subn_get_portinfo(smp, ibdev, port);
+
+done:
+       return ret;
+}
+
+static int recv_subn_set_pkeytable(struct ib_smp *smp,
+                                  struct ib_device *ibdev)
+{
+       u32 startpx = 32 * (be32_to_cpu(smp->attr_mod) & 0xffff);
+       __be16 *p = (__be16 *) smp->data;
+       u16 *q = (u16 *) smp->data;
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       unsigned i, n = ipath_layer_get_npkeys(dev->dd);
+
+       for (i = 0; i < n; i++)
+               q[i] = be16_to_cpu(p[i]);
+
+       if (startpx != 0 ||
+           ipath_layer_set_pkeys(dev->dd, q) != 0)
+               smp->status |= IB_SMP_INVALID_FIELD;
+
+       return recv_subn_get_pkeytable(smp, ibdev);
+}
+
+#define IB_PMA_CLASS_PORT_INFO         __constant_htons(0x0001)
+#define IB_PMA_PORT_SAMPLES_CONTROL    __constant_htons(0x0010)
+#define IB_PMA_PORT_SAMPLES_RESULT     __constant_htons(0x0011)
+#define IB_PMA_PORT_COUNTERS           __constant_htons(0x0012)
+#define IB_PMA_PORT_COUNTERS_EXT       __constant_htons(0x001D)
+#define IB_PMA_PORT_SAMPLES_RESULT_EXT __constant_htons(0x001E)
+
+struct ib_perf {
+       u8 base_version;
+       u8 mgmt_class;
+       u8 class_version;
+       u8 method;
+       __be16 status;
+       __be16 unused;
+       __be64 tid;
+       __be16 attr_id;
+       __be16 resv;
+       __be32 attr_mod;
+       u8 reserved[40];
+       u8 data[192];
+} __attribute__ ((packed));
+
+struct ib_pma_classportinfo {
+       u8 base_version;
+       u8 class_version;
+       __be16 cap_mask;
+       u8 reserved[3];
+       u8 resp_time_value;     /* only lower 5 bits */
+       union ib_gid redirect_gid;
+       __be32 redirect_tc_sl_fl;       /* 8, 4, 20 bits respectively */
+       __be16 redirect_lid;
+       __be16 redirect_pkey;
+       __be32 redirect_qp;     /* only lower 24 bits */
+       __be32 redirect_qkey;
+       union ib_gid trap_gid;
+       __be32 trap_tc_sl_fl;   /* 8, 4, 20 bits respectively */
+       __be16 trap_lid;
+       __be16 trap_pkey;
+       __be32 trap_hl_qp;      /* 8, 24 bits respectively */
+       __be32 trap_qkey;
+} __attribute__ ((packed));
+
+struct ib_pma_portsamplescontrol {
+       u8 opcode;
+       u8 port_select;
+       u8 tick;
+       u8 counter_width;       /* only lower 3 bits */
+       __be32 counter_mask0_9; /* 2, 10 * 3, bits */
+       __be16 counter_mask10_14;       /* 1, 5 * 3, bits */
+       u8 sample_mechanisms;
+       u8 sample_status;       /* only lower 2 bits */
+       __be64 option_mask;
+       __be64 vendor_mask;
+       __be32 sample_start;
+       __be32 sample_interval;
+       __be16 tag;
+       __be16 counter_select[15];
+} __attribute__ ((packed));
+
+struct ib_pma_portsamplesresult {
+       __be16 tag;
+       __be16 sample_status;   /* only lower 2 bits */
+       __be32 counter[15];
+} __attribute__ ((packed));
+
+struct ib_pma_portsamplesresult_ext {
+       __be16 tag;
+       __be16 sample_status;   /* only lower 2 bits */
+       __be32 extended_width;  /* only upper 2 bits */
+       __be64 counter[15];
+} __attribute__ ((packed));
+
+struct ib_pma_portcounters {
+       u8 reserved;
+       u8 port_select;
+       __be16 counter_select;
+       __be16 symbol_error_counter;
+       u8 link_error_recovery_counter;
+       u8 link_downed_counter;
+       __be16 port_rcv_errors;
+       __be16 port_rcv_remphys_errors;
+       __be16 port_rcv_switch_relay_errors;
+       __be16 port_xmit_discards;
+       u8 port_xmit_constraint_errors;
+       u8 port_rcv_constraint_errors;
+       u8 reserved1;
+       u8 lli_ebor_errors;     /* 4, 4, bits */
+       __be16 reserved2;
+       __be16 vl15_dropped;
+       __be32 port_xmit_data;
+       __be32 port_rcv_data;
+       __be32 port_xmit_packets;
+       __be32 port_rcv_packets;
+} __attribute__ ((packed));
+
+#define IB_PMA_SEL_SYMBOL_ERROR                        __constant_htons(0x0001)
+#define IB_PMA_SEL_LINK_ERROR_RECOVERY         __constant_htons(0x0002)
+#define IB_PMA_SEL_LINK_DOWNED                 __constant_htons(0x0004)
+#define IB_PMA_SEL_PORT_RCV_ERRORS             __constant_htons(0x0008)
+#define IB_PMA_SEL_PORT_RCV_REMPHYS_ERRORS     __constant_htons(0x0010)
+#define IB_PMA_SEL_PORT_XMIT_DISCARDS          __constant_htons(0x0040)
+#define IB_PMA_SEL_PORT_XMIT_DATA              __constant_htons(0x1000)
+#define IB_PMA_SEL_PORT_RCV_DATA               __constant_htons(0x2000)
+#define IB_PMA_SEL_PORT_XMIT_PACKETS           __constant_htons(0x4000)
+#define IB_PMA_SEL_PORT_RCV_PACKETS            __constant_htons(0x8000)
+
+struct ib_pma_portcounters_ext {
+       u8 reserved;
+       u8 port_select;
+       __be16 counter_select;
+       __be32 reserved1;
+       __be64 port_xmit_data;
+       __be64 port_rcv_data;
+       __be64 port_xmit_packets;
+       __be64 port_rcv_packets;
+       __be64 port_unicast_xmit_packets;
+       __be64 port_unicast_rcv_packets;
+       __be64 port_multicast_xmit_packets;
+       __be64 port_multicast_rcv_packets;
+} __attribute__ ((packed));
+
+#define IB_PMA_SELX_PORT_XMIT_DATA             __constant_htons(0x0001)
+#define IB_PMA_SELX_PORT_RCV_DATA              __constant_htons(0x0002)
+#define IB_PMA_SELX_PORT_XMIT_PACKETS          __constant_htons(0x0004)
+#define IB_PMA_SELX_PORT_RCV_PACKETS           __constant_htons(0x0008)
+#define IB_PMA_SELX_PORT_UNI_XMIT_PACKETS      __constant_htons(0x0010)
+#define IB_PMA_SELX_PORT_UNI_RCV_PACKETS       __constant_htons(0x0020)
+#define IB_PMA_SELX_PORT_MULTI_XMIT_PACKETS    __constant_htons(0x0040)
+#define IB_PMA_SELX_PORT_MULTI_RCV_PACKETS     __constant_htons(0x0080)
+
+static int recv_pma_get_classportinfo(struct ib_perf *pmp)
+{
+       struct ib_pma_classportinfo *p =
+               (struct ib_pma_classportinfo *)pmp->data;
+
+       memset(pmp->data, 0, sizeof(pmp->data));
+
+       if (pmp->attr_mod != 0)
+               pmp->status |= IB_SMP_INVALID_FIELD;
+
+       /* Indicate AllPortSelect is valid (only one port anyway) */
+       p->cap_mask = __constant_cpu_to_be16(1 << 8);
+       p->base_version = 1;
+       p->class_version = 1;
+       /*
+        * Expected response time is 4.096 usec. * 2^18 == 1.073741824
+        * sec.
+        */
+       p->resp_time_value = 18;
+
+       return reply((struct ib_smp *) pmp);
+}
+
+/*
+ * The PortSamplesControl.CounterMasks field is an array of 3 bit fields
+ * which specify the N'th counter's capabilities. See ch. 16.1.3.2.
+ * We support 5 counters which only count the mandatory quantities.
+ */
+#define COUNTER_MASK(q, n) (q << ((9 - n) * 3))
+#define COUNTER_MASK0_9 \
+       __constant_cpu_to_be32(COUNTER_MASK(1, 0) | \
+                              COUNTER_MASK(1, 1) | \
+                              COUNTER_MASK(1, 2) | \
+                              COUNTER_MASK(1, 3) | \
+                              COUNTER_MASK(1, 4))
+
+static int recv_pma_get_portsamplescontrol(struct ib_perf *pmp,
+                                          struct ib_device *ibdev, u8 port)
+{
+       struct ib_pma_portsamplescontrol *p =
+               (struct ib_pma_portsamplescontrol *)pmp->data;
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       unsigned long flags;
+       u8 port_select = p->port_select;
+
+       memset(pmp->data, 0, sizeof(pmp->data));
+
+       p->port_select = port_select;
+       if (pmp->attr_mod != 0 ||
+           (port_select != port && port_select != 0xFF))
+               pmp->status |= IB_SMP_INVALID_FIELD;
+       /*
+        * Ticks are 10x the link transfer period which for 2.5Gbs is 4
+        * nsec.  0 == 4 nsec., 1 == 8 nsec., ..., 255 == 1020 nsec.  Sample
+        * intervals are counted in ticks.  Since we use Linux timers, that
+        * count in jiffies, we can't sample for less than 1000 ticks if HZ
+        * == 1000 (4000 ticks if HZ is 250).
+        */
+       /* XXX This is WRONG. */
+       p->tick = 250;          /* 1 usec. */
+       p->counter_width = 4;   /* 32 bit counters */
+       p->counter_mask0_9 = COUNTER_MASK0_9;
+       spin_lock_irqsave(&dev->pending_lock, flags);
+       p->sample_status = dev->pma_sample_status;
+       p->sample_start = cpu_to_be32(dev->pma_sample_start);
+       p->sample_interval = cpu_to_be32(dev->pma_sample_interval);
+       p->tag = cpu_to_be16(dev->pma_tag);
+       p->counter_select[0] = dev->pma_counter_select[0];
+       p->counter_select[1] = dev->pma_counter_select[1];
+       p->counter_select[2] = dev->pma_counter_select[2];
+       p->counter_select[3] = dev->pma_counter_select[3];
+       p->counter_select[4] = dev->pma_counter_select[4];
+       spin_unlock_irqrestore(&dev->pending_lock, flags);
+
+       return reply((struct ib_smp *) pmp);
+}
+
+static int recv_pma_set_portsamplescontrol(struct ib_perf *pmp,
+                                          struct ib_device *ibdev, u8 port)
+{
+       struct ib_pma_portsamplescontrol *p =
+               (struct ib_pma_portsamplescontrol *)pmp->data;
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       unsigned long flags;
+       u32 start;
+       int ret;
+
+       if (pmp->attr_mod != 0 ||
+           (p->port_select != port && p->port_select != 0xFF)) {
+               pmp->status |= IB_SMP_INVALID_FIELD;
+               ret = reply((struct ib_smp *) pmp);
+               goto bail;
+       }
+
+       start = be32_to_cpu(p->sample_start);
+       if (start != 0) {
+               spin_lock_irqsave(&dev->pending_lock, flags);
+               if (dev->pma_sample_status == IB_PMA_SAMPLE_STATUS_DONE) {
+                       dev->pma_sample_status =
+                               IB_PMA_SAMPLE_STATUS_STARTED;
+                       dev->pma_sample_start = start;
+                       dev->pma_sample_interval =
+                               be32_to_cpu(p->sample_interval);
+                       dev->pma_tag = be16_to_cpu(p->tag);
+                       if (p->counter_select[0])
+                               dev->pma_counter_select[0] =
+                                       p->counter_select[0];
+                       if (p->counter_select[1])
+                               dev->pma_counter_select[1] =
+                                       p->counter_select[1];
+                       if (p->counter_select[2])
+                               dev->pma_counter_select[2] =
+                                       p->counter_select[2];
+                       if (p->counter_select[3])
+                               dev->pma_counter_select[3] =
+                                       p->counter_select[3];
+                       if (p->counter_select[4])
+                               dev->pma_counter_select[4] =
+                                       p->counter_select[4];
+               }
+               spin_unlock_irqrestore(&dev->pending_lock, flags);
+       }
+       ret = recv_pma_get_portsamplescontrol(pmp, ibdev, port);
+
+bail:
+       return ret;
+}
+
+static u64 get_counter(struct ipath_ibdev *dev, __be16 sel)
+{
+       u64 ret;
+
+       switch (sel) {
+       case IB_PMA_PORT_XMIT_DATA:
+               ret = dev->ipath_sword;
+               break;
+       case IB_PMA_PORT_RCV_DATA:
+               ret = dev->ipath_rword;
+               break;
+       case IB_PMA_PORT_XMIT_PKTS:
+               ret = dev->ipath_spkts;
+               break;
+       case IB_PMA_PORT_RCV_PKTS:
+               ret = dev->ipath_rpkts;
+               break;
+       case IB_PMA_PORT_XMIT_WAIT:
+               ret = dev->ipath_xmit_wait;
+               break;
+       default:
+               ret = 0;
+       }
+
+       return ret;
+}
+
+static int recv_pma_get_portsamplesresult(struct ib_perf *pmp,
+                                         struct ib_device *ibdev)
+{
+       struct ib_pma_portsamplesresult *p =
+               (struct ib_pma_portsamplesresult *)pmp->data;
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       int i;
+
+       memset(pmp->data, 0, sizeof(pmp->data));
+       p->tag = cpu_to_be16(dev->pma_tag);
+       p->sample_status = cpu_to_be16(dev->pma_sample_status);
+       for (i = 0; i < ARRAY_SIZE(dev->pma_counter_select); i++)
+               p->counter[i] = cpu_to_be32(
+                       get_counter(dev, dev->pma_counter_select[i]));
+
+       return reply((struct ib_smp *) pmp);
+}
+
+static int recv_pma_get_portsamplesresult_ext(struct ib_perf *pmp,
+                                             struct ib_device *ibdev)
+{
+       struct ib_pma_portsamplesresult_ext *p =
+               (struct ib_pma_portsamplesresult_ext *)pmp->data;
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       int i;
+
+       memset(pmp->data, 0, sizeof(pmp->data));
+       p->tag = cpu_to_be16(dev->pma_tag);
+       p->sample_status = cpu_to_be16(dev->pma_sample_status);
+       /* 64 bits */
+       p->extended_width = __constant_cpu_to_be32(0x80000000);
+       for (i = 0; i < ARRAY_SIZE(dev->pma_counter_select); i++)
+               p->counter[i] = cpu_to_be64(
+                       get_counter(dev, dev->pma_counter_select[i]));
+
+       return reply((struct ib_smp *) pmp);
+}
+
+static int recv_pma_get_portcounters(struct ib_perf *pmp,
+                                    struct ib_device *ibdev, u8 port)
+{
+       struct ib_pma_portcounters *p = (struct ib_pma_portcounters *)
+               pmp->data;
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       struct ipath_layer_counters cntrs;
+       u8 port_select = p->port_select;
+
+       ipath_layer_get_counters(dev->dd, &cntrs);
+
+       /* Adjust counters for any resets done. */
+       cntrs.symbol_error_counter -= dev->n_symbol_error_counter;
+       cntrs.link_error_recovery_counter -=
+               dev->n_link_error_recovery_counter;
+       cntrs.link_downed_counter -= dev->n_link_downed_counter;
+       cntrs.port_rcv_errors += dev->rcv_errors;
+       cntrs.port_rcv_errors -= dev->n_port_rcv_errors;
+       cntrs.port_rcv_remphys_errors -= dev->n_port_rcv_remphys_errors;
+       cntrs.port_xmit_discards -= dev->n_port_xmit_discards;
+       cntrs.port_xmit_data -= dev->n_port_xmit_data;
+       cntrs.port_rcv_data -= dev->n_port_rcv_data;
+       cntrs.port_xmit_packets -= dev->n_port_xmit_packets;
+       cntrs.port_rcv_packets -= dev->n_port_rcv_packets;
+
+       memset(pmp->data, 0, sizeof(pmp->data));
+
+       p->port_select = port_select;
+       if (pmp->attr_mod != 0 ||
+           (port_select != port && port_select != 0xFF))
+               pmp->status |= IB_SMP_INVALID_FIELD;
+
+       if (cntrs.symbol_error_counter > 0xFFFFUL)
+               p->symbol_error_counter = __constant_cpu_to_be16(0xFFFF);
+       else
+               p->symbol_error_counter =
+                       cpu_to_be16((u16)cntrs.symbol_error_counter);
+       if (cntrs.link_error_recovery_counter > 0xFFUL)
+               p->link_error_recovery_counter = 0xFF;
+       else
+               p->link_error_recovery_counter =
+                       (u8)cntrs.link_error_recovery_counter;
+       if (cntrs.link_downed_counter > 0xFFUL)
+               p->link_downed_counter = 0xFF;
+       else
+               p->link_downed_counter = (u8)cntrs.link_downed_counter;
+       if (cntrs.port_rcv_errors > 0xFFFFUL)
+               p->port_rcv_errors = __constant_cpu_to_be16(0xFFFF);
+       else
+               p->port_rcv_errors =
+                       cpu_to_be16((u16) cntrs.port_rcv_errors);
+       if (cntrs.port_rcv_remphys_errors > 0xFFFFUL)
+               p->port_rcv_remphys_errors = __constant_cpu_to_be16(0xFFFF);
+       else
+               p->port_rcv_remphys_errors =
+                       cpu_to_be16((u16)cntrs.port_rcv_remphys_errors);
+       if (cntrs.port_xmit_discards > 0xFFFFUL)
+               p->port_xmit_discards = __constant_cpu_to_be16(0xFFFF);
+       else
+               p->port_xmit_discards =
+                       cpu_to_be16((u16)cntrs.port_xmit_discards);
+       if (cntrs.port_xmit_data > 0xFFFFFFFFUL)
+               p->port_xmit_data = __constant_cpu_to_be32(0xFFFFFFFF);
+       else
+               p->port_xmit_data = cpu_to_be32((u32)cntrs.port_xmit_data);
+       if (cntrs.port_rcv_data > 0xFFFFFFFFUL)
+               p->port_rcv_data = __constant_cpu_to_be32(0xFFFFFFFF);
+       else
+               p->port_rcv_data = cpu_to_be32((u32)cntrs.port_rcv_data);
+       if (cntrs.port_xmit_packets > 0xFFFFFFFFUL)
+               p->port_xmit_packets = __constant_cpu_to_be32(0xFFFFFFFF);
+       else
+               p->port_xmit_packets =
+                       cpu_to_be32((u32)cntrs.port_xmit_packets);
+       if (cntrs.port_rcv_packets > 0xFFFFFFFFUL)
+               p->port_rcv_packets = __constant_cpu_to_be32(0xFFFFFFFF);
+       else
+               p->port_rcv_packets =
+                       cpu_to_be32((u32) cntrs.port_rcv_packets);
+
+       return reply((struct ib_smp *) pmp);
+}
+
+static int recv_pma_get_portcounters_ext(struct ib_perf *pmp,
+                                        struct ib_device *ibdev, u8 port)
+{
+       struct ib_pma_portcounters_ext *p =
+               (struct ib_pma_portcounters_ext *)pmp->data;
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       u64 swords, rwords, spkts, rpkts, xwait;
+       u8 port_select = p->port_select;
+
+       ipath_layer_snapshot_counters(dev->dd, &swords, &rwords, &spkts,
+                                     &rpkts, &xwait);
+
+       /* Adjust counters for any resets done. */
+       swords -= dev->n_port_xmit_data;
+       rwords -= dev->n_port_rcv_data;
+       spkts -= dev->n_port_xmit_packets;
+       rpkts -= dev->n_port_rcv_packets;
+
+       memset(pmp->data, 0, sizeof(pmp->data));
+
+       p->port_select = port_select;
+       if (pmp->attr_mod != 0 ||
+           (port_select != port && port_select != 0xFF))
+               pmp->status |= IB_SMP_INVALID_FIELD;
+
+       p->port_xmit_data = cpu_to_be64(swords);
+       p->port_rcv_data = cpu_to_be64(rwords);
+       p->port_xmit_packets = cpu_to_be64(spkts);
+       p->port_rcv_packets = cpu_to_be64(rpkts);
+       p->port_unicast_xmit_packets = cpu_to_be64(dev->n_unicast_xmit);
+       p->port_unicast_rcv_packets = cpu_to_be64(dev->n_unicast_rcv);
+       p->port_multicast_xmit_packets = cpu_to_be64(dev->n_multicast_xmit);
+       p->port_multicast_rcv_packets = cpu_to_be64(dev->n_multicast_rcv);
+
+       return reply((struct ib_smp *) pmp);
+}
+
+static int recv_pma_set_portcounters(struct ib_perf *pmp,
+                                    struct ib_device *ibdev, u8 port)
+{
+       struct ib_pma_portcounters *p = (struct ib_pma_portcounters *)
+               pmp->data;
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       struct ipath_layer_counters cntrs;
+
+       /*
+        * Since the HW doesn't support clearing counters, we save the
+        * current count and subtract it from future responses.
+        */
+       ipath_layer_get_counters(dev->dd, &cntrs);
+
+       if (p->counter_select & IB_PMA_SEL_SYMBOL_ERROR)
+               dev->n_symbol_error_counter = cntrs.symbol_error_counter;
+
+       if (p->counter_select & IB_PMA_SEL_LINK_ERROR_RECOVERY)
+               dev->n_link_error_recovery_counter =
+                       cntrs.link_error_recovery_counter;
+
+       if (p->counter_select & IB_PMA_SEL_LINK_DOWNED)
+               dev->n_link_downed_counter = cntrs.link_downed_counter;
+
+       if (p->counter_select & IB_PMA_SEL_PORT_RCV_ERRORS)
+               dev->n_port_rcv_errors =
+                       cntrs.port_rcv_errors + dev->rcv_errors;
+
+       if (p->counter_select & IB_PMA_SEL_PORT_RCV_REMPHYS_ERRORS)
+               dev->n_port_rcv_remphys_errors =
+                       cntrs.port_rcv_remphys_errors;
+
+       if (p->counter_select & IB_PMA_SEL_PORT_XMIT_DISCARDS)
+               dev->n_port_xmit_discards = cntrs.port_xmit_discards;
+
+       if (p->counter_select & IB_PMA_SEL_PORT_XMIT_DATA)
+               dev->n_port_xmit_data = cntrs.port_xmit_data;
+
+       if (p->counter_select & IB_PMA_SEL_PORT_RCV_DATA)
+               dev->n_port_rcv_data = cntrs.port_rcv_data;
+
+       if (p->counter_select & IB_PMA_SEL_PORT_XMIT_PACKETS)
+               dev->n_port_xmit_packets = cntrs.port_xmit_packets;
+
+       if (p->counter_select & IB_PMA_SEL_PORT_RCV_PACKETS)
+               dev->n_port_rcv_packets = cntrs.port_rcv_packets;
+
+       return recv_pma_get_portcounters(pmp, ibdev, port);
+}
+
+static int recv_pma_set_portcounters_ext(struct ib_perf *pmp,
+                                        struct ib_device *ibdev, u8 port)
+{
+       struct ib_pma_portcounters *p = (struct ib_pma_portcounters *)
+               pmp->data;
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       u64 swords, rwords, spkts, rpkts, xwait;
+
+       ipath_layer_snapshot_counters(dev->dd, &swords, &rwords, &spkts,
+                                     &rpkts, &xwait);
+
+       if (p->counter_select & IB_PMA_SELX_PORT_XMIT_DATA)
+               dev->n_port_xmit_data = swords;
+
+       if (p->counter_select & IB_PMA_SELX_PORT_RCV_DATA)
+               dev->n_port_rcv_data = rwords;
+
+       if (p->counter_select & IB_PMA_SELX_PORT_XMIT_PACKETS)
+               dev->n_port_xmit_packets = spkts;
+
+       if (p->counter_select & IB_PMA_SELX_PORT_RCV_PACKETS)
+               dev->n_port_rcv_packets = rpkts;
+
+       if (p->counter_select & IB_PMA_SELX_PORT_UNI_XMIT_PACKETS)
+               dev->n_unicast_xmit = 0;
+
+       if (p->counter_select & IB_PMA_SELX_PORT_UNI_RCV_PACKETS)
+               dev->n_unicast_rcv = 0;
+
+       if (p->counter_select & IB_PMA_SELX_PORT_MULTI_XMIT_PACKETS)
+               dev->n_multicast_xmit = 0;
+
+       if (p->counter_select & IB_PMA_SELX_PORT_MULTI_RCV_PACKETS)
+               dev->n_multicast_rcv = 0;
+
+       return recv_pma_get_portcounters_ext(pmp, ibdev, port);
+}
+
+static int process_subn(struct ib_device *ibdev, int mad_flags,
+                       u8 port_num, struct ib_mad *in_mad,
+                       struct ib_mad *out_mad)
+{
+       struct ib_smp *smp = (struct ib_smp *)out_mad;
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       int ret;
+
+       *out_mad = *in_mad;
+       if (smp->class_version != 1) {
+               smp->status |= IB_SMP_UNSUP_VERSION;
+               ret = reply(smp);
+               goto bail;
+       }
+
+       /* Is the mkey in the process of expiring? */
+       if (dev->mkey_lease_timeout && jiffies >= dev->mkey_lease_timeout) {
+               /* Clear timeout and mkey protection field. */
+               dev->mkey_lease_timeout = 0;
+               dev->mkeyprot_resv_lmc &= 0x3F;
+       }
+
+       /*
+        * M_Key checking depends on
+        * Portinfo:M_Key_protect_bits
+        */
+       if ((mad_flags & IB_MAD_IGNORE_MKEY) == 0 && dev->mkey != 0 &&
+           dev->mkey != smp->mkey &&
+           (smp->method == IB_MGMT_METHOD_SET ||
+            (smp->method == IB_MGMT_METHOD_GET &&
+             (dev->mkeyprot_resv_lmc >> 7) != 0))) {
+               if (dev->mkey_violations != 0xFFFF)
+                       ++dev->mkey_violations;
+               if (dev->mkey_lease_timeout ||
+                   dev->mkey_lease_period == 0) {
+                       ret = IB_MAD_RESULT_SUCCESS |
+                               IB_MAD_RESULT_CONSUMED;
+                       goto bail;
+               }
+               dev->mkey_lease_timeout = jiffies +
+                       dev->mkey_lease_period * HZ;
+               /* Future: Generate a trap notice. */
+               ret = IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
+               goto bail;
+       } else if (dev->mkey_lease_timeout)
+               dev->mkey_lease_timeout = 0;
+
+       switch (smp->method) {
+       case IB_MGMT_METHOD_GET:
+               switch (smp->attr_id) {
+               case IB_SMP_ATTR_NODE_DESC:
+                       ret = recv_subn_get_nodedescription(smp, ibdev);
+                       goto bail;
+               case IB_SMP_ATTR_NODE_INFO:
+                       ret = recv_subn_get_nodeinfo(smp, ibdev, port_num);
+                       goto bail;
+               case IB_SMP_ATTR_GUID_INFO:
+                       ret = recv_subn_get_guidinfo(smp, ibdev);
+                       goto bail;
+               case IB_SMP_ATTR_PORT_INFO:
+                       ret = recv_subn_get_portinfo(smp, ibdev, port_num);
+                       goto bail;
+               case IB_SMP_ATTR_PKEY_TABLE:
+                       ret = recv_subn_get_pkeytable(smp, ibdev);
+                       goto bail;
+               case IB_SMP_ATTR_SM_INFO:
+                       if (dev->port_cap_flags & IB_PORT_SM_DISABLED) {
+                               ret = IB_MAD_RESULT_SUCCESS |
+                                       IB_MAD_RESULT_CONSUMED;
+                               goto bail;
+                       }
+                       if (dev->port_cap_flags & IB_PORT_SM) {
+                               ret = IB_MAD_RESULT_SUCCESS;
+                               goto bail;
+                       }
+                       /* FALLTHROUGH */
+               default:
+                       smp->status |= IB_SMP_UNSUP_METH_ATTR;
+                       ret = reply(smp);
+                       goto bail;
+               }
+
+       case IB_MGMT_METHOD_SET:
+               switch (smp->attr_id) {
+               case IB_SMP_ATTR_GUID_INFO:
+                       ret = recv_subn_set_guidinfo(smp, ibdev);
+                       goto bail;
+               case IB_SMP_ATTR_PORT_INFO:
+                       ret = recv_subn_set_portinfo(smp, ibdev, port_num);
+                       goto bail;
+               case IB_SMP_ATTR_PKEY_TABLE:
+                       ret = recv_subn_set_pkeytable(smp, ibdev);
+                       goto bail;
+               case IB_SMP_ATTR_SM_INFO:
+                       if (dev->port_cap_flags & IB_PORT_SM_DISABLED) {
+                               ret = IB_MAD_RESULT_SUCCESS |
+                                       IB_MAD_RESULT_CONSUMED;
+                               goto bail;
+                       }
+                       if (dev->port_cap_flags & IB_PORT_SM) {
+                               ret = IB_MAD_RESULT_SUCCESS;
+                               goto bail;
+                       }
+                       /* FALLTHROUGH */
+               default:
+                       smp->status |= IB_SMP_UNSUP_METH_ATTR;
+                       ret = reply(smp);
+                       goto bail;
+               }
+
+       case IB_MGMT_METHOD_GET_RESP:
+               /*
+                * The ib_mad module will call us to process responses
+                * before checking for other consumers.
+                * Just tell the caller to process it normally.
+                */
+               ret = IB_MAD_RESULT_FAILURE;
+               goto bail;
+       default:
+               smp->status |= IB_SMP_UNSUP_METHOD;
+               ret = reply(smp);
+       }
+
+bail:
+       return ret;
+}
+
+static int process_perf(struct ib_device *ibdev, u8 port_num,
+                       struct ib_mad *in_mad,
+                       struct ib_mad *out_mad)
+{
+       struct ib_perf *pmp = (struct ib_perf *)out_mad;
+       int ret;
+
+       *out_mad = *in_mad;
+       if (pmp->class_version != 1) {
+               pmp->status |= IB_SMP_UNSUP_VERSION;
+               ret = reply((struct ib_smp *) pmp);
+               goto bail;
+       }
+
+       switch (pmp->method) {
+       case IB_MGMT_METHOD_GET:
+               switch (pmp->attr_id) {
+               case IB_PMA_CLASS_PORT_INFO:
+                       ret = recv_pma_get_classportinfo(pmp);
+                       goto bail;
+               case IB_PMA_PORT_SAMPLES_CONTROL:
+                       ret = recv_pma_get_portsamplescontrol(pmp, ibdev,
+                                                             port_num);
+                       goto bail;
+               case IB_PMA_PORT_SAMPLES_RESULT:
+                       ret = recv_pma_get_portsamplesresult(pmp, ibdev);
+                       goto bail;
+               case IB_PMA_PORT_SAMPLES_RESULT_EXT:
+                       ret = recv_pma_get_portsamplesresult_ext(pmp,
+                                                                ibdev);
+                       goto bail;
+               case IB_PMA_PORT_COUNTERS:
+                       ret = recv_pma_get_portcounters(pmp, ibdev,
+                                                       port_num);
+                       goto bail;
+               case IB_PMA_PORT_COUNTERS_EXT:
+                       ret = recv_pma_get_portcounters_ext(pmp, ibdev,
+                                                           port_num);
+                       goto bail;
+               default:
+                       pmp->status |= IB_SMP_UNSUP_METH_ATTR;
+                       ret = reply((struct ib_smp *) pmp);
+                       goto bail;
+               }
+
+       case IB_MGMT_METHOD_SET:
+               switch (pmp->attr_id) {
+               case IB_PMA_PORT_SAMPLES_CONTROL:
+                       ret = recv_pma_set_portsamplescontrol(pmp, ibdev,
+                                                             port_num);
+                       goto bail;
+               case IB_PMA_PORT_COUNTERS:
+                       ret = recv_pma_set_portcounters(pmp, ibdev,
+                                                       port_num);
+                       goto bail;
+               case IB_PMA_PORT_COUNTERS_EXT:
+                       ret = recv_pma_set_portcounters_ext(pmp, ibdev,
+                                                           port_num);
+                       goto bail;
+               default:
+                       pmp->status |= IB_SMP_UNSUP_METH_ATTR;
+                       ret = reply((struct ib_smp *) pmp);
+                       goto bail;
+               }
+
+       case IB_MGMT_METHOD_GET_RESP:
+               /*
+                * The ib_mad module will call us to process responses
+                * before checking for other consumers.
+                * Just tell the caller to process it normally.
+                */
+               ret = IB_MAD_RESULT_FAILURE;
+               goto bail;
+       default:
+               pmp->status |= IB_SMP_UNSUP_METHOD;
+               ret = reply((struct ib_smp *) pmp);
+       }
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_process_mad - process an incoming MAD packet
+ * @ibdev: the infiniband device this packet came in on
+ * @mad_flags: MAD flags
+ * @port_num: the port number this packet came in on
+ * @in_wc: the work completion entry for this packet
+ * @in_grh: the global route header for this packet
+ * @in_mad: the incoming MAD
+ * @out_mad: any outgoing MAD reply
+ *
+ * Returns IB_MAD_RESULT_SUCCESS if this is a MAD that we are not
+ * interested in processing.
+ *
+ * Note that the verbs framework has already done the MAD sanity checks,
+ * and hop count/pointer updating for IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE
+ * MADs.
+ *
+ * This is called by the ib_mad module.
+ */
+int ipath_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
+                     struct ib_wc *in_wc, struct ib_grh *in_grh,
+                     struct ib_mad *in_mad, struct ib_mad *out_mad)
+{
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       int ret;
+
+       /*
+        * Snapshot current HW counters to "clear" them.
+        * This should be done when the driver is loaded except that for
+        * some reason we get a zillion errors when brining up the link.
+        */
+       if (dev->rcv_errors == 0) {
+               struct ipath_layer_counters cntrs;
+
+               ipath_layer_get_counters(to_idev(ibdev)->dd, &cntrs);
+               dev->rcv_errors++;
+               dev->n_symbol_error_counter = cntrs.symbol_error_counter;
+               dev->n_link_error_recovery_counter =
+                       cntrs.link_error_recovery_counter;
+               dev->n_link_downed_counter = cntrs.link_downed_counter;
+               dev->n_port_rcv_errors = cntrs.port_rcv_errors + 1;
+               dev->n_port_rcv_remphys_errors =
+                       cntrs.port_rcv_remphys_errors;
+               dev->n_port_xmit_discards = cntrs.port_xmit_discards;
+               dev->n_port_xmit_data = cntrs.port_xmit_data;
+               dev->n_port_rcv_data = cntrs.port_rcv_data;
+               dev->n_port_xmit_packets = cntrs.port_xmit_packets;
+               dev->n_port_rcv_packets = cntrs.port_rcv_packets;
+       }
+       switch (in_mad->mad_hdr.mgmt_class) {
+       case IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE:
+       case IB_MGMT_CLASS_SUBN_LID_ROUTED:
+               ret = process_subn(ibdev, mad_flags, port_num,
+                                  in_mad, out_mad);
+               goto bail;
+       case IB_MGMT_CLASS_PERF_MGMT:
+               ret = process_perf(ibdev, port_num, in_mad, out_mad);
+               goto bail;
+       default:
+               ret = IB_MAD_RESULT_SUCCESS;
+       }
+
+bail:
+       return ret;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c
new file mode 100644 (file)
index 0000000..69ffec6
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <rdma/ib_pack.h>
+#include <rdma/ib_smi.h>
+
+#include "ipath_verbs.h"
+
+/**
+ * ipath_get_dma_mr - get a DMA memory region
+ * @pd: protection domain for this memory region
+ * @acc: access flags
+ *
+ * Returns the memory region on success, otherwise returns an errno.
+ */
+struct ib_mr *ipath_get_dma_mr(struct ib_pd *pd, int acc)
+{
+       struct ipath_mr *mr;
+       struct ib_mr *ret;
+
+       mr = kzalloc(sizeof *mr, GFP_KERNEL);
+       if (!mr) {
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       mr->mr.access_flags = acc;
+       ret = &mr->ibmr;
+
+bail:
+       return ret;
+}
+
+static struct ipath_mr *alloc_mr(int count,
+                                struct ipath_lkey_table *lk_table)
+{
+       struct ipath_mr *mr;
+       int m, i = 0;
+
+       /* Allocate struct plus pointers to first level page tables. */
+       m = (count + IPATH_SEGSZ - 1) / IPATH_SEGSZ;
+       mr = kmalloc(sizeof *mr + m * sizeof mr->mr.map[0], GFP_KERNEL);
+       if (!mr)
+               goto done;
+
+       /* Allocate first level page tables. */
+       for (; i < m; i++) {
+               mr->mr.map[i] = kmalloc(sizeof *mr->mr.map[0], GFP_KERNEL);
+               if (!mr->mr.map[i])
+                       goto bail;
+       }
+       mr->mr.mapsz = m;
+
+       /*
+        * ib_reg_phys_mr() will initialize mr->ibmr except for
+        * lkey and rkey.
+        */
+       if (!ipath_alloc_lkey(lk_table, &mr->mr))
+               goto bail;
+       mr->ibmr.rkey = mr->ibmr.lkey = mr->mr.lkey;
+
+       goto done;
+
+bail:
+       while (i) {
+               i--;
+               kfree(mr->mr.map[i]);
+       }
+       kfree(mr);
+       mr = NULL;
+
+done:
+       return mr;
+}
+
+/**
+ * ipath_reg_phys_mr - register a physical memory region
+ * @pd: protection domain for this memory region
+ * @buffer_list: pointer to the list of physical buffers to register
+ * @num_phys_buf: the number of physical buffers to register
+ * @iova_start: the starting address passed over IB which maps to this MR
+ *
+ * Returns the memory region on success, otherwise returns an errno.
+ */
+struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd,
+                               struct ib_phys_buf *buffer_list,
+                               int num_phys_buf, int acc, u64 *iova_start)
+{
+       struct ipath_mr *mr;
+       int n, m, i;
+       struct ib_mr *ret;
+
+       mr = alloc_mr(num_phys_buf, &to_idev(pd->device)->lk_table);
+       if (mr == NULL) {
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       mr->mr.user_base = *iova_start;
+       mr->mr.iova = *iova_start;
+       mr->mr.length = 0;
+       mr->mr.offset = 0;
+       mr->mr.access_flags = acc;
+       mr->mr.max_segs = num_phys_buf;
+
+       m = 0;
+       n = 0;
+       for (i = 0; i < num_phys_buf; i++) {
+               mr->mr.map[m]->segs[n].vaddr =
+                       phys_to_virt(buffer_list[i].addr);
+               mr->mr.map[m]->segs[n].length = buffer_list[i].size;
+               mr->mr.length += buffer_list[i].size;
+               n++;
+               if (n == IPATH_SEGSZ) {
+                       m++;
+                       n = 0;
+               }
+       }
+
+       ret = &mr->ibmr;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_reg_user_mr - register a userspace memory region
+ * @pd: protection domain for this memory region
+ * @region: the user memory region
+ * @mr_access_flags: access flags for this memory region
+ * @udata: unused by the InfiniPath driver
+ *
+ * Returns the memory region on success, otherwise returns an errno.
+ */
+struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region,
+                               int mr_access_flags, struct ib_udata *udata)
+{
+       struct ipath_mr *mr;
+       struct ib_umem_chunk *chunk;
+       int n, m, i;
+       struct ib_mr *ret;
+
+       n = 0;
+       list_for_each_entry(chunk, &region->chunk_list, list)
+               n += chunk->nents;
+
+       mr = alloc_mr(n, &to_idev(pd->device)->lk_table);
+       if (!mr) {
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       mr->mr.user_base = region->user_base;
+       mr->mr.iova = region->virt_base;
+       mr->mr.length = region->length;
+       mr->mr.offset = region->offset;
+       mr->mr.access_flags = mr_access_flags;
+       mr->mr.max_segs = n;
+
+       m = 0;
+       n = 0;
+       list_for_each_entry(chunk, &region->chunk_list, list) {
+               for (i = 0; i < chunk->nmap; i++) {
+                       mr->mr.map[m]->segs[n].vaddr =
+                               page_address(chunk->page_list[i].page);
+                       mr->mr.map[m]->segs[n].length = region->page_size;
+                       n++;
+                       if (n == IPATH_SEGSZ) {
+                               m++;
+                               n = 0;
+                       }
+               }
+       }
+       ret = &mr->ibmr;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_dereg_mr - unregister and free a memory region
+ * @ibmr: the memory region to free
+ *
+ * Returns 0 on success.
+ *
+ * Note that this is called to free MRs created by ipath_get_dma_mr()
+ * or ipath_reg_user_mr().
+ */
+int ipath_dereg_mr(struct ib_mr *ibmr)
+{
+       struct ipath_mr *mr = to_imr(ibmr);
+       int i;
+
+       ipath_free_lkey(&to_idev(ibmr->device)->lk_table, ibmr->lkey);
+       i = mr->mr.mapsz;
+       while (i) {
+               i--;
+               kfree(mr->mr.map[i]);
+       }
+       kfree(mr);
+       return 0;
+}
+
+/**
+ * ipath_alloc_fmr - allocate a fast memory region
+ * @pd: the protection domain for this memory region
+ * @mr_access_flags: access flags for this memory region
+ * @fmr_attr: fast memory region attributes
+ *
+ * Returns the memory region on success, otherwise returns an errno.
+ */
+struct ib_fmr *ipath_alloc_fmr(struct ib_pd *pd, int mr_access_flags,
+                              struct ib_fmr_attr *fmr_attr)
+{
+       struct ipath_fmr *fmr;
+       int m, i = 0;
+       struct ib_fmr *ret;
+
+       /* Allocate struct plus pointers to first level page tables. */
+       m = (fmr_attr->max_pages + IPATH_SEGSZ - 1) / IPATH_SEGSZ;
+       fmr = kmalloc(sizeof *fmr + m * sizeof fmr->mr.map[0], GFP_KERNEL);
+       if (!fmr)
+               goto bail;
+
+       /* Allocate first level page tables. */
+       for (; i < m; i++) {
+               fmr->mr.map[i] = kmalloc(sizeof *fmr->mr.map[0],
+                                        GFP_KERNEL);
+               if (!fmr->mr.map[i])
+                       goto bail;
+       }
+       fmr->mr.mapsz = m;
+
+       /*
+        * ib_alloc_fmr() will initialize fmr->ibfmr except for lkey &
+        * rkey.
+        */
+       if (!ipath_alloc_lkey(&to_idev(pd->device)->lk_table, &fmr->mr))
+               goto bail;
+       fmr->ibfmr.rkey = fmr->ibfmr.lkey = fmr->mr.lkey;
+       /*
+        * Resources are allocated but no valid mapping (RKEY can't be
+        * used).
+        */
+       fmr->mr.user_base = 0;
+       fmr->mr.iova = 0;
+       fmr->mr.length = 0;
+       fmr->mr.offset = 0;
+       fmr->mr.access_flags = mr_access_flags;
+       fmr->mr.max_segs = fmr_attr->max_pages;
+       fmr->page_shift = fmr_attr->page_shift;
+
+       ret = &fmr->ibfmr;
+       goto done;
+
+bail:
+       while (i)
+               kfree(fmr->mr.map[--i]);
+       kfree(fmr);
+       ret = ERR_PTR(-ENOMEM);
+
+done:
+       return ret;
+}
+
+/**
+ * ipath_map_phys_fmr - set up a fast memory region
+ * @ibmfr: the fast memory region to set up
+ * @page_list: the list of pages to associate with the fast memory region
+ * @list_len: the number of pages to associate with the fast memory region
+ * @iova: the virtual address of the start of the fast memory region
+ *
+ * This may be called from interrupt context.
+ */
+
+int ipath_map_phys_fmr(struct ib_fmr *ibfmr, u64 * page_list,
+                      int list_len, u64 iova)
+{
+       struct ipath_fmr *fmr = to_ifmr(ibfmr);
+       struct ipath_lkey_table *rkt;
+       unsigned long flags;
+       int m, n, i;
+       u32 ps;
+       int ret;
+
+       if (list_len > fmr->mr.max_segs) {
+               ret = -EINVAL;
+               goto bail;
+       }
+       rkt = &to_idev(ibfmr->device)->lk_table;
+       spin_lock_irqsave(&rkt->lock, flags);
+       fmr->mr.user_base = iova;
+       fmr->mr.iova = iova;
+       ps = 1 << fmr->page_shift;
+       fmr->mr.length = list_len * ps;
+       m = 0;
+       n = 0;
+       ps = 1 << fmr->page_shift;
+       for (i = 0; i < list_len; i++) {
+               fmr->mr.map[m]->segs[n].vaddr = phys_to_virt(page_list[i]);
+               fmr->mr.map[m]->segs[n].length = ps;
+               if (++n == IPATH_SEGSZ) {
+                       m++;
+                       n = 0;
+               }
+       }
+       spin_unlock_irqrestore(&rkt->lock, flags);
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_unmap_fmr - unmap fast memory regions
+ * @fmr_list: the list of fast memory regions to unmap
+ *
+ * Returns 0 on success.
+ */
+int ipath_unmap_fmr(struct list_head *fmr_list)
+{
+       struct ipath_fmr *fmr;
+       struct ipath_lkey_table *rkt;
+       unsigned long flags;
+
+       list_for_each_entry(fmr, fmr_list, ibfmr.list) {
+               rkt = &to_idev(fmr->ibfmr.device)->lk_table;
+               spin_lock_irqsave(&rkt->lock, flags);
+               fmr->mr.user_base = 0;
+               fmr->mr.iova = 0;
+               fmr->mr.length = 0;
+               spin_unlock_irqrestore(&rkt->lock, flags);
+       }
+       return 0;
+}
+
+/**
+ * ipath_dealloc_fmr - deallocate a fast memory region
+ * @ibfmr: the fast memory region to deallocate
+ *
+ * Returns 0 on success.
+ */
+int ipath_dealloc_fmr(struct ib_fmr *ibfmr)
+{
+       struct ipath_fmr *fmr = to_ifmr(ibfmr);
+       int i;
+
+       ipath_free_lkey(&to_idev(ibfmr->device)->lk_table, ibfmr->lkey);
+       i = fmr->mr.mapsz;
+       while (i)
+               kfree(fmr->mr.map[--i]);
+       kfree(fmr);
+       return 0;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_pe800.c b/drivers/infiniband/hw/ipath/ipath_pe800.c
new file mode 100644 (file)
index 0000000..e693a7a
--- /dev/null
@@ -0,0 +1,1247 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+/*
+ * This file contains all of the code that is specific to the
+ * InfiniPath PE-800 chip.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+
+#include "ipath_kernel.h"
+#include "ipath_registers.h"
+
+/*
+ * This file contains all the chip-specific register information and
+ * access functions for the PathScale PE800, the PCI-Express chip.
+ *
+ * This lists the InfiniPath PE800 registers, in the actual chip layout.
+ * This structure should never be directly accessed.
+ */
+struct _infinipath_do_not_use_kernel_regs {
+       unsigned long long Revision;
+       unsigned long long Control;
+       unsigned long long PageAlign;
+       unsigned long long PortCnt;
+       unsigned long long DebugPortSelect;
+       unsigned long long Reserved0;
+       unsigned long long SendRegBase;
+       unsigned long long UserRegBase;
+       unsigned long long CounterRegBase;
+       unsigned long long Scratch;
+       unsigned long long Reserved1;
+       unsigned long long Reserved2;
+       unsigned long long IntBlocked;
+       unsigned long long IntMask;
+       unsigned long long IntStatus;
+       unsigned long long IntClear;
+       unsigned long long ErrorMask;
+       unsigned long long ErrorStatus;
+       unsigned long long ErrorClear;
+       unsigned long long HwErrMask;
+       unsigned long long HwErrStatus;
+       unsigned long long HwErrClear;
+       unsigned long long HwDiagCtrl;
+       unsigned long long MDIO;
+       unsigned long long IBCStatus;
+       unsigned long long IBCCtrl;
+       unsigned long long ExtStatus;
+       unsigned long long ExtCtrl;
+       unsigned long long GPIOOut;
+       unsigned long long GPIOMask;
+       unsigned long long GPIOStatus;
+       unsigned long long GPIOClear;
+       unsigned long long RcvCtrl;
+       unsigned long long RcvBTHQP;
+       unsigned long long RcvHdrSize;
+       unsigned long long RcvHdrCnt;
+       unsigned long long RcvHdrEntSize;
+       unsigned long long RcvTIDBase;
+       unsigned long long RcvTIDCnt;
+       unsigned long long RcvEgrBase;
+       unsigned long long RcvEgrCnt;
+       unsigned long long RcvBufBase;
+       unsigned long long RcvBufSize;
+       unsigned long long RxIntMemBase;
+       unsigned long long RxIntMemSize;
+       unsigned long long RcvPartitionKey;
+       unsigned long long Reserved3;
+       unsigned long long RcvPktLEDCnt;
+       unsigned long long Reserved4[8];
+       unsigned long long SendCtrl;
+       unsigned long long SendPIOBufBase;
+       unsigned long long SendPIOSize;
+       unsigned long long SendPIOBufCnt;
+       unsigned long long SendPIOAvailAddr;
+       unsigned long long TxIntMemBase;
+       unsigned long long TxIntMemSize;
+       unsigned long long Reserved5;
+       unsigned long long PCIeRBufTestReg0;
+       unsigned long long PCIeRBufTestReg1;
+       unsigned long long Reserved51[6];
+       unsigned long long SendBufferError;
+       unsigned long long SendBufferErrorCONT1;
+       unsigned long long Reserved6SBE[6];
+       unsigned long long RcvHdrAddr0;
+       unsigned long long RcvHdrAddr1;
+       unsigned long long RcvHdrAddr2;
+       unsigned long long RcvHdrAddr3;
+       unsigned long long RcvHdrAddr4;
+       unsigned long long Reserved7RHA[11];
+       unsigned long long RcvHdrTailAddr0;
+       unsigned long long RcvHdrTailAddr1;
+       unsigned long long RcvHdrTailAddr2;
+       unsigned long long RcvHdrTailAddr3;
+       unsigned long long RcvHdrTailAddr4;
+       unsigned long long Reserved8RHTA[11];
+       unsigned long long Reserved9SW[8];
+       unsigned long long SerdesConfig0;
+       unsigned long long SerdesConfig1;
+       unsigned long long SerdesStatus;
+       unsigned long long XGXSConfig;
+       unsigned long long IBPLLCfg;
+       unsigned long long Reserved10SW2[3];
+       unsigned long long PCIEQ0SerdesConfig0;
+       unsigned long long PCIEQ0SerdesConfig1;
+       unsigned long long PCIEQ0SerdesStatus;
+       unsigned long long Reserved11;
+       unsigned long long PCIEQ1SerdesConfig0;
+       unsigned long long PCIEQ1SerdesConfig1;
+       unsigned long long PCIEQ1SerdesStatus;
+       unsigned long long Reserved12;
+};
+
+#define IPATH_KREG_OFFSET(field) (offsetof(struct \
+    _infinipath_do_not_use_kernel_regs, field) / sizeof(u64))
+#define IPATH_CREG_OFFSET(field) (offsetof( \
+    struct infinipath_counters, field) / sizeof(u64))
+
+static const struct ipath_kregs ipath_pe_kregs = {
+       .kr_control = IPATH_KREG_OFFSET(Control),
+       .kr_counterregbase = IPATH_KREG_OFFSET(CounterRegBase),
+       .kr_debugportselect = IPATH_KREG_OFFSET(DebugPortSelect),
+       .kr_errorclear = IPATH_KREG_OFFSET(ErrorClear),
+       .kr_errormask = IPATH_KREG_OFFSET(ErrorMask),
+       .kr_errorstatus = IPATH_KREG_OFFSET(ErrorStatus),
+       .kr_extctrl = IPATH_KREG_OFFSET(ExtCtrl),
+       .kr_extstatus = IPATH_KREG_OFFSET(ExtStatus),
+       .kr_gpio_clear = IPATH_KREG_OFFSET(GPIOClear),
+       .kr_gpio_mask = IPATH_KREG_OFFSET(GPIOMask),
+       .kr_gpio_out = IPATH_KREG_OFFSET(GPIOOut),
+       .kr_gpio_status = IPATH_KREG_OFFSET(GPIOStatus),
+       .kr_hwdiagctrl = IPATH_KREG_OFFSET(HwDiagCtrl),
+       .kr_hwerrclear = IPATH_KREG_OFFSET(HwErrClear),
+       .kr_hwerrmask = IPATH_KREG_OFFSET(HwErrMask),
+       .kr_hwerrstatus = IPATH_KREG_OFFSET(HwErrStatus),
+       .kr_ibcctrl = IPATH_KREG_OFFSET(IBCCtrl),
+       .kr_ibcstatus = IPATH_KREG_OFFSET(IBCStatus),
+       .kr_intblocked = IPATH_KREG_OFFSET(IntBlocked),
+       .kr_intclear = IPATH_KREG_OFFSET(IntClear),
+       .kr_intmask = IPATH_KREG_OFFSET(IntMask),
+       .kr_intstatus = IPATH_KREG_OFFSET(IntStatus),
+       .kr_mdio = IPATH_KREG_OFFSET(MDIO),
+       .kr_pagealign = IPATH_KREG_OFFSET(PageAlign),
+       .kr_partitionkey = IPATH_KREG_OFFSET(RcvPartitionKey),
+       .kr_portcnt = IPATH_KREG_OFFSET(PortCnt),
+       .kr_rcvbthqp = IPATH_KREG_OFFSET(RcvBTHQP),
+       .kr_rcvbufbase = IPATH_KREG_OFFSET(RcvBufBase),
+       .kr_rcvbufsize = IPATH_KREG_OFFSET(RcvBufSize),
+       .kr_rcvctrl = IPATH_KREG_OFFSET(RcvCtrl),
+       .kr_rcvegrbase = IPATH_KREG_OFFSET(RcvEgrBase),
+       .kr_rcvegrcnt = IPATH_KREG_OFFSET(RcvEgrCnt),
+       .kr_rcvhdrcnt = IPATH_KREG_OFFSET(RcvHdrCnt),
+       .kr_rcvhdrentsize = IPATH_KREG_OFFSET(RcvHdrEntSize),
+       .kr_rcvhdrsize = IPATH_KREG_OFFSET(RcvHdrSize),
+       .kr_rcvintmembase = IPATH_KREG_OFFSET(RxIntMemBase),
+       .kr_rcvintmemsize = IPATH_KREG_OFFSET(RxIntMemSize),
+       .kr_rcvtidbase = IPATH_KREG_OFFSET(RcvTIDBase),
+       .kr_rcvtidcnt = IPATH_KREG_OFFSET(RcvTIDCnt),
+       .kr_revision = IPATH_KREG_OFFSET(Revision),
+       .kr_scratch = IPATH_KREG_OFFSET(Scratch),
+       .kr_sendbuffererror = IPATH_KREG_OFFSET(SendBufferError),
+       .kr_sendctrl = IPATH_KREG_OFFSET(SendCtrl),
+       .kr_sendpioavailaddr = IPATH_KREG_OFFSET(SendPIOAvailAddr),
+       .kr_sendpiobufbase = IPATH_KREG_OFFSET(SendPIOBufBase),
+       .kr_sendpiobufcnt = IPATH_KREG_OFFSET(SendPIOBufCnt),
+       .kr_sendpiosize = IPATH_KREG_OFFSET(SendPIOSize),
+       .kr_sendregbase = IPATH_KREG_OFFSET(SendRegBase),
+       .kr_txintmembase = IPATH_KREG_OFFSET(TxIntMemBase),
+       .kr_txintmemsize = IPATH_KREG_OFFSET(TxIntMemSize),
+       .kr_userregbase = IPATH_KREG_OFFSET(UserRegBase),
+       .kr_serdesconfig0 = IPATH_KREG_OFFSET(SerdesConfig0),
+       .kr_serdesconfig1 = IPATH_KREG_OFFSET(SerdesConfig1),
+       .kr_serdesstatus = IPATH_KREG_OFFSET(SerdesStatus),
+       .kr_xgxsconfig = IPATH_KREG_OFFSET(XGXSConfig),
+       .kr_ibpllcfg = IPATH_KREG_OFFSET(IBPLLCfg),
+
+       /*
+        * These should not be used directly via ipath_read_kreg64(),
+        * use them with ipath_read_kreg64_port()
+        */
+       .kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0),
+       .kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0),
+
+       /* This group is pe-800-specific; and used only in this file */
+       /* The rcvpktled register controls one of the debug port signals, so
+        * a packet activity LED can be connected to it. */
+       .kr_rcvpktledcnt = IPATH_KREG_OFFSET(RcvPktLEDCnt),
+       .kr_pcierbuftestreg0 = IPATH_KREG_OFFSET(PCIeRBufTestReg0),
+       .kr_pcierbuftestreg1 = IPATH_KREG_OFFSET(PCIeRBufTestReg1),
+       .kr_pcieq0serdesconfig0 = IPATH_KREG_OFFSET(PCIEQ0SerdesConfig0),
+       .kr_pcieq0serdesconfig1 = IPATH_KREG_OFFSET(PCIEQ0SerdesConfig1),
+       .kr_pcieq0serdesstatus = IPATH_KREG_OFFSET(PCIEQ0SerdesStatus),
+       .kr_pcieq1serdesconfig0 = IPATH_KREG_OFFSET(PCIEQ1SerdesConfig0),
+       .kr_pcieq1serdesconfig1 = IPATH_KREG_OFFSET(PCIEQ1SerdesConfig1),
+       .kr_pcieq1serdesstatus = IPATH_KREG_OFFSET(PCIEQ1SerdesStatus)
+};
+
+static const struct ipath_cregs ipath_pe_cregs = {
+       .cr_badformatcnt = IPATH_CREG_OFFSET(RxBadFormatCnt),
+       .cr_erricrccnt = IPATH_CREG_OFFSET(RxICRCErrCnt),
+       .cr_errlinkcnt = IPATH_CREG_OFFSET(RxLinkProblemCnt),
+       .cr_errlpcrccnt = IPATH_CREG_OFFSET(RxLPCRCErrCnt),
+       .cr_errpkey = IPATH_CREG_OFFSET(RxPKeyMismatchCnt),
+       .cr_errrcvflowctrlcnt = IPATH_CREG_OFFSET(RxFlowCtrlErrCnt),
+       .cr_err_rlencnt = IPATH_CREG_OFFSET(RxLenErrCnt),
+       .cr_errslencnt = IPATH_CREG_OFFSET(TxLenErrCnt),
+       .cr_errtidfull = IPATH_CREG_OFFSET(RxTIDFullErrCnt),
+       .cr_errtidvalid = IPATH_CREG_OFFSET(RxTIDValidErrCnt),
+       .cr_errvcrccnt = IPATH_CREG_OFFSET(RxVCRCErrCnt),
+       .cr_ibstatuschange = IPATH_CREG_OFFSET(IBStatusChangeCnt),
+       .cr_intcnt = IPATH_CREG_OFFSET(LBIntCnt),
+       .cr_invalidrlencnt = IPATH_CREG_OFFSET(RxMaxMinLenErrCnt),
+       .cr_invalidslencnt = IPATH_CREG_OFFSET(TxMaxMinLenErrCnt),
+       .cr_lbflowstallcnt = IPATH_CREG_OFFSET(LBFlowStallCnt),
+       .cr_pktrcvcnt = IPATH_CREG_OFFSET(RxDataPktCnt),
+       .cr_pktrcvflowctrlcnt = IPATH_CREG_OFFSET(RxFlowPktCnt),
+       .cr_pktsendcnt = IPATH_CREG_OFFSET(TxDataPktCnt),
+       .cr_pktsendflowcnt = IPATH_CREG_OFFSET(TxFlowPktCnt),
+       .cr_portovflcnt = IPATH_CREG_OFFSET(RxP0HdrEgrOvflCnt),
+       .cr_rcvebpcnt = IPATH_CREG_OFFSET(RxEBPCnt),
+       .cr_rcvovflcnt = IPATH_CREG_OFFSET(RxBufOvflCnt),
+       .cr_senddropped = IPATH_CREG_OFFSET(TxDroppedPktCnt),
+       .cr_sendstallcnt = IPATH_CREG_OFFSET(TxFlowStallCnt),
+       .cr_sendunderruncnt = IPATH_CREG_OFFSET(TxUnderrunCnt),
+       .cr_wordrcvcnt = IPATH_CREG_OFFSET(RxDwordCnt),
+       .cr_wordsendcnt = IPATH_CREG_OFFSET(TxDwordCnt),
+       .cr_unsupvlcnt = IPATH_CREG_OFFSET(TxUnsupVLErrCnt),
+       .cr_rxdroppktcnt = IPATH_CREG_OFFSET(RxDroppedPktCnt),
+       .cr_iblinkerrrecovcnt = IPATH_CREG_OFFSET(IBLinkErrRecoveryCnt),
+       .cr_iblinkdowncnt = IPATH_CREG_OFFSET(IBLinkDownedCnt),
+       .cr_ibsymbolerrcnt = IPATH_CREG_OFFSET(IBSymbolErrCnt)
+};
+
+/* kr_intstatus, kr_intclear, kr_intmask bits */
+#define INFINIPATH_I_RCVURG_MASK 0x1F
+#define INFINIPATH_I_RCVAVAIL_MASK 0x1F
+
+/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
+#define INFINIPATH_HWE_PCIEMEMPARITYERR_MASK  0x000000000000003fULL
+#define INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT 0
+#define INFINIPATH_HWE_PCIEPOISONEDTLP      0x0000000010000000ULL
+#define INFINIPATH_HWE_PCIECPLTIMEOUT       0x0000000020000000ULL
+#define INFINIPATH_HWE_PCIEBUSPARITYXTLH    0x0000000040000000ULL
+#define INFINIPATH_HWE_PCIEBUSPARITYXADM    0x0000000080000000ULL
+#define INFINIPATH_HWE_PCIEBUSPARITYRADM    0x0000000100000000ULL
+#define INFINIPATH_HWE_COREPLL_FBSLIP       0x0080000000000000ULL
+#define INFINIPATH_HWE_COREPLL_RFSLIP       0x0100000000000000ULL
+#define INFINIPATH_HWE_PCIE1PLLFAILED       0x0400000000000000ULL
+#define INFINIPATH_HWE_PCIE0PLLFAILED       0x0800000000000000ULL
+#define INFINIPATH_HWE_SERDESPLLFAILED      0x1000000000000000ULL
+
+/* kr_extstatus bits */
+#define INFINIPATH_EXTS_FREQSEL 0x2
+#define INFINIPATH_EXTS_SERDESSEL 0x4
+#define INFINIPATH_EXTS_MEMBIST_ENDTEST     0x0000000000004000
+#define INFINIPATH_EXTS_MEMBIST_FOUND       0x0000000000008000
+
+#define _IPATH_GPIO_SDA_NUM 1
+#define _IPATH_GPIO_SCL_NUM 0
+
+#define IPATH_GPIO_SDA (1ULL << \
+       (_IPATH_GPIO_SDA_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT))
+#define IPATH_GPIO_SCL (1ULL << \
+       (_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT))
+
+/**
+ * ipath_pe_handle_hwerrors - display hardware errors.
+ * @dd: the infinipath device
+ * @msg: the output buffer
+ * @msgl: the size of the output buffer
+ *
+ * Use same msg buffer as regular errors to avoid excessive stack
+ * use.  Most hardware errors are catastrophic, but for right now,
+ * we'll print them and continue.  We reuse the same message buffer as
+ * ipath_handle_errors() to avoid excessive stack usage.
+ */
+void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
+       size_t msgl)
+{
+       ipath_err_t hwerrs;
+       u32 bits, ctrl;
+       int isfatal = 0;
+       char bitsmsg[64];
+
+       hwerrs = ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwerrstatus);
+       if (!hwerrs) {
+               /*
+                * better than printing cofusing messages
+                * This seems to be related to clearing the crc error, or
+                * the pll error during init.
+                */
+               ipath_cdbg(VERBOSE, "Called but no hardware errors set\n");
+               return;
+       } else if (hwerrs == ~0ULL) {
+               ipath_dev_err(dd, "Read of hardware error status failed "
+                             "(all bits set); ignoring\n");
+               return;
+       }
+       ipath_stats.sps_hwerrs++;
+
+       /* Always clear the error status register, except MEMBISTFAIL,
+        * regardless of whether we continue or stop using the chip.
+        * We want that set so we know it failed, even across driver reload.
+        * We'll still ignore it in the hwerrmask.  We do this partly for
+        * diagnostics, but also for support */
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear,
+                        hwerrs&~INFINIPATH_HWE_MEMBISTFAILED);
+
+       hwerrs &= dd->ipath_hwerrmask;
+
+       /*
+        * make sure we get this much out, unless told to be quiet,
+        * or it's occurred within the last 5 seconds
+        */
+       if ((hwerrs & ~dd->ipath_lasthwerror) ||
+           (ipath_debug & __IPATH_VERBDBG))
+               dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx "
+                        "(cleared)\n", (unsigned long long) hwerrs);
+       dd->ipath_lasthwerror |= hwerrs;
+
+       if (hwerrs & ~infinipath_hwe_bitsextant)
+               ipath_dev_err(dd, "hwerror interrupt with unknown errors "
+                             "%llx set\n", (unsigned long long)
+                             (hwerrs & ~infinipath_hwe_bitsextant));
+
+       ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control);
+       if (ctrl & INFINIPATH_C_FREEZEMODE) {
+               if (hwerrs) {
+                       /*
+                        * if any set that we aren't ignoring only make the
+                        * complaint once, in case it's stuck or recurring,
+                        * and we get here multiple times
+                        */
+                       if (dd->ipath_flags & IPATH_INITTED) {
+                               ipath_dev_err(dd, "Fatal Error (freeze "
+                                             "mode), no longer usable\n");
+                               isfatal = 1;
+                       }
+                       /*
+                        * Mark as having had an error for driver, and also
+                        * for /sys and status word mapped to user programs.
+                        * This marks unit as not usable, until reset
+                        */
+                       *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
+                       *dd->ipath_statusp |= IPATH_STATUS_HWERROR;
+                       dd->ipath_flags &= ~IPATH_INITTED;
+               } else {
+                       ipath_dbg("Clearing freezemode on ignored hardware "
+                                 "error\n");
+                       ctrl &= ~INFINIPATH_C_FREEZEMODE;
+                       ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
+                                        ctrl);
+               }
+       }
+
+       *msg = '\0';
+
+       if (hwerrs & INFINIPATH_HWE_MEMBISTFAILED) {
+               strlcat(msg, "[Memory BIST test failed, PE-800 unusable]",
+                       msgl);
+               /* ignore from now on, so disable until driver reloaded */
+               *dd->ipath_statusp |= IPATH_STATUS_HWERROR;
+               dd->ipath_hwerrmask &= ~INFINIPATH_HWE_MEMBISTFAILED;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask,
+                                dd->ipath_hwerrmask);
+       }
+       if (hwerrs & (INFINIPATH_HWE_RXEMEMPARITYERR_MASK
+                     << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)) {
+               bits = (u32) ((hwerrs >>
+                              INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) &
+                             INFINIPATH_HWE_RXEMEMPARITYERR_MASK);
+               snprintf(bitsmsg, sizeof bitsmsg, "[RXE Parity Errs %x] ",
+                        bits);
+               strlcat(msg, bitsmsg, msgl);
+       }
+       if (hwerrs & (INFINIPATH_HWE_TXEMEMPARITYERR_MASK
+                     << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
+               bits = (u32) ((hwerrs >>
+                              INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) &
+                             INFINIPATH_HWE_TXEMEMPARITYERR_MASK);
+               snprintf(bitsmsg, sizeof bitsmsg, "[TXE Parity Errs %x] ",
+                        bits);
+               strlcat(msg, bitsmsg, msgl);
+       }
+       if (hwerrs & (INFINIPATH_HWE_PCIEMEMPARITYERR_MASK
+                     << INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT)) {
+               bits = (u32) ((hwerrs >>
+                              INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT) &
+                             INFINIPATH_HWE_PCIEMEMPARITYERR_MASK);
+               snprintf(bitsmsg, sizeof bitsmsg,
+                        "[PCIe Mem Parity Errs %x] ", bits);
+               strlcat(msg, bitsmsg, msgl);
+       }
+       if (hwerrs & INFINIPATH_HWE_IBCBUSTOSPCPARITYERR)
+               strlcat(msg, "[IB2IPATH Parity]", msgl);
+       if (hwerrs & INFINIPATH_HWE_IBCBUSFRSPCPARITYERR)
+               strlcat(msg, "[IPATH2IB Parity]", msgl);
+
+#define _IPATH_PLL_FAIL (INFINIPATH_HWE_COREPLL_FBSLIP |       \
+                        INFINIPATH_HWE_COREPLL_RFSLIP )
+
+       if (hwerrs & _IPATH_PLL_FAIL) {
+               snprintf(bitsmsg, sizeof bitsmsg,
+                        "[PLL failed (%llx), PE-800 unusable]",
+                        (unsigned long long) hwerrs & _IPATH_PLL_FAIL);
+               strlcat(msg, bitsmsg, msgl);
+               /* ignore from now on, so disable until driver reloaded */
+               dd->ipath_hwerrmask &= ~(hwerrs & _IPATH_PLL_FAIL);
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask,
+                                dd->ipath_hwerrmask);
+       }
+
+       if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED) {
+               /*
+                * If it occurs, it is left masked since the eternal
+                * interface is unused
+                */
+               dd->ipath_hwerrmask &= ~INFINIPATH_HWE_SERDESPLLFAILED;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask,
+                                dd->ipath_hwerrmask);
+       }
+
+       if (hwerrs & INFINIPATH_HWE_PCIEPOISONEDTLP)
+               strlcat(msg, "[PCIe Poisoned TLP]", msgl);
+       if (hwerrs & INFINIPATH_HWE_PCIECPLTIMEOUT)
+               strlcat(msg, "[PCIe completion timeout]", msgl);
+
+       /*
+        * In practice, it's unlikely wthat we'll see PCIe PLL, or bus
+        * parity or memory parity error failures, because most likely we
+        * won't be able to talk to the core of the chip.  Nonetheless, we
+        * might see them, if they are in parts of the PCIe core that aren't
+        * essential.
+        */
+       if (hwerrs & INFINIPATH_HWE_PCIE1PLLFAILED)
+               strlcat(msg, "[PCIePLL1]", msgl);
+       if (hwerrs & INFINIPATH_HWE_PCIE0PLLFAILED)
+               strlcat(msg, "[PCIePLL0]", msgl);
+       if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYXTLH)
+               strlcat(msg, "[PCIe XTLH core parity]", msgl);
+       if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYXADM)
+               strlcat(msg, "[PCIe ADM TX core parity]", msgl);
+       if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYRADM)
+               strlcat(msg, "[PCIe ADM RX core parity]", msgl);
+
+       if (hwerrs & INFINIPATH_HWE_RXDSYNCMEMPARITYERR)
+               strlcat(msg, "[Rx Dsync]", msgl);
+       if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED)
+               strlcat(msg, "[SerDes PLL]", msgl);
+
+       ipath_dev_err(dd, "%s hardware error\n", msg);
+       if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) {
+               /*
+                * for /sys status file ; if no trailing } is copied, we'll
+                * know it was truncated.
+                */
+               snprintf(dd->ipath_freezemsg, dd->ipath_freezelen,
+                        "{%s}", msg);
+       }
+}
+
+/**
+ * ipath_pe_boardname - fill in the board name
+ * @dd: the infinipath device
+ * @name: the output buffer
+ * @namelen: the size of the output buffer
+ *
+ * info is based on the board revision register
+ */
+static int ipath_pe_boardname(struct ipath_devdata *dd, char *name,
+                             size_t namelen)
+{
+       char *n = NULL;
+       u8 boardrev = dd->ipath_boardrev;
+       int ret;
+
+       switch (boardrev) {
+       case 0:
+               n = "InfiniPath_Emulation";
+               break;
+       case 1:
+               n = "InfiniPath_PE-800-Bringup";
+               break;
+       case 2:
+               n = "InfiniPath_PE-880";
+               break;
+       case 3:
+               n = "InfiniPath_PE-850";
+               break;
+       case 4:
+               n = "InfiniPath_PE-860";
+               break;
+       default:
+               ipath_dev_err(dd,
+                             "Don't yet know about board with ID %u\n",
+                             boardrev);
+               snprintf(name, namelen, "Unknown_InfiniPath_PE-8xx_%u",
+                        boardrev);
+               break;
+       }
+       if (n)
+               snprintf(name, namelen, "%s", n);
+
+       if (dd->ipath_majrev != 4 || dd->ipath_minrev != 1) {
+               ipath_dev_err(dd, "Unsupported PE-800 revision %u.%u!\n",
+                             dd->ipath_majrev, dd->ipath_minrev);
+               ret = 1;
+       } else
+               ret = 0;
+
+       return ret;
+}
+
+/**
+ * ipath_pe_init_hwerrors - enable hardware errors
+ * @dd: the infinipath device
+ *
+ * now that we have finished initializing everything that might reasonably
+ * cause a hardware error, and cleared those errors bits as they occur,
+ * we can enable hardware errors in the mask (potentially enabling
+ * freeze mode), and enable hardware errors as errors (along with
+ * everything else) in errormask
+ */
+void ipath_pe_init_hwerrors(struct ipath_devdata *dd)
+{
+       ipath_err_t val;
+       u64 extsval;
+
+       extsval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus);
+
+       if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST))
+               ipath_dev_err(dd, "MemBIST did not complete!\n");
+
+       val = ~0ULL;    /* barring bugs, all hwerrors become interrupts, */
+
+       if (!dd->ipath_boardrev)        // no PLL for Emulator
+               val &= ~INFINIPATH_HWE_SERDESPLLFAILED;
+
+       /* workaround bug 9460 in internal interface bus parity checking */
+       val &= ~INFINIPATH_HWE_PCIEBUSPARITYRADM;
+
+       dd->ipath_hwerrmask = val;
+}
+
+/**
+ * ipath_pe_bringup_serdes - bring up the serdes
+ * @dd: the infinipath device
+ */
+int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
+{
+       u64 val, tmp, config1;
+       int ret = 0, change = 0;
+
+       ipath_dbg("Trying to bringup serdes\n");
+
+       if (ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwerrstatus) &
+           INFINIPATH_HWE_SERDESPLLFAILED) {
+               ipath_dbg("At start, serdes PLL failed bit set "
+                         "in hwerrstatus, clearing and continuing\n");
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear,
+                                INFINIPATH_HWE_SERDESPLLFAILED);
+       }
+
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
+       config1 = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig1);
+
+       ipath_cdbg(VERBOSE, "SerDes status config0=%llx config1=%llx, "
+                  "xgxsconfig %llx\n", (unsigned long long) val,
+                  (unsigned long long) config1, (unsigned long long)
+                  ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig));
+
+       /*
+        * Force reset on, also set rxdetect enable.  Must do before reading
+        * serdesstatus at least for simulation, or some of the bits in
+        * serdes status will come back as undefined and cause simulation
+        * failures
+        */
+       val |= INFINIPATH_SERDC0_RESET_PLL | INFINIPATH_SERDC0_RXDETECT_EN
+               | INFINIPATH_SERDC0_L1PWR_DN;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val);
+       /* be sure chip saw it */
+       tmp = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+       udelay(5);              /* need pll reset set at least for a bit */
+       /*
+        * after PLL is reset, set the per-lane Resets and TxIdle and
+        * clear the PLL reset and rxdetect (to get falling edge).
+        * Leave L1PWR bits set (permanently)
+        */
+       val &= ~(INFINIPATH_SERDC0_RXDETECT_EN | INFINIPATH_SERDC0_RESET_PLL
+                | INFINIPATH_SERDC0_L1PWR_DN);
+       val |= INFINIPATH_SERDC0_RESET_MASK | INFINIPATH_SERDC0_TXIDLE;
+       ipath_cdbg(VERBOSE, "Clearing pll reset and setting lane resets "
+                  "and txidle (%llx)\n", (unsigned long long) val);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val);
+       /* be sure chip saw it */
+       tmp = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+       /* need PLL reset clear for at least 11 usec before lane
+        * resets cleared; give it a few more to be sure */
+       udelay(15);
+       val &= ~(INFINIPATH_SERDC0_RESET_MASK | INFINIPATH_SERDC0_TXIDLE);
+
+       ipath_cdbg(VERBOSE, "Clearing lane resets and txidle "
+                  "(writing %llx)\n", (unsigned long long) val);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val);
+       /* be sure chip saw it */
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig);
+       if (((val >> INFINIPATH_XGXS_MDIOADDR_SHIFT) &
+            INFINIPATH_XGXS_MDIOADDR_MASK) != 3) {
+               val &=
+                       ~(INFINIPATH_XGXS_MDIOADDR_MASK <<
+                         INFINIPATH_XGXS_MDIOADDR_SHIFT);
+               /* MDIO address 3 */
+               val |= 3ULL << INFINIPATH_XGXS_MDIOADDR_SHIFT;
+               change = 1;
+       }
+       if (val & INFINIPATH_XGXS_RESET) {
+               val &= ~INFINIPATH_XGXS_RESET;
+               change = 1;
+       }
+       if (change)
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, val);
+
+       val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
+
+       /* clear current and de-emphasis bits */
+       config1 &= ~0x0ffffffff00ULL;
+       /* set current to 20ma */
+       config1 |= 0x00000000000ULL;
+       /* set de-emphasis to -5.68dB */
+       config1 |= 0x0cccc000000ULL;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig1, config1);
+
+       ipath_cdbg(VERBOSE, "done: SerDes status config0=%llx "
+                  "config1=%llx, sstatus=%llx xgxs=%llx\n",
+                  (unsigned long long) val, (unsigned long long) config1,
+                  (unsigned long long)
+                  ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesstatus),
+                  (unsigned long long)
+                  ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig));
+
+       if (!ipath_waitfor_mdio_cmdready(dd)) {
+               ipath_write_kreg(
+                       dd, dd->ipath_kregs->kr_mdio,
+                       ipath_mdio_req(IPATH_MDIO_CMD_READ, 31,
+                                      IPATH_MDIO_CTRL_XGXS_REG_8, 0));
+               if (ipath_waitfor_complete(dd, dd->ipath_kregs->kr_mdio,
+                                          IPATH_MDIO_DATAVALID, &val))
+                       ipath_dbg("Never got MDIO data for XGXS "
+                                 "status read\n");
+               else
+                       ipath_cdbg(VERBOSE, "MDIO Read reg8, "
+                                  "'bank' 31 %x\n", (u32) val);
+       } else
+               ipath_dbg("Never got MDIO cmdready for XGXS status read\n");
+
+       return ret;
+}
+
+/**
+ * ipath_pe_quiet_serdes - set serdes to txidle
+ * @dd: the infinipath device
+ * Called when driver is being unloaded
+ */
+void ipath_pe_quiet_serdes(struct ipath_devdata *dd)
+{
+       u64 val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
+
+       val |= INFINIPATH_SERDC0_TXIDLE;
+       ipath_dbg("Setting TxIdleEn on serdes (config0 = %llx)\n",
+                 (unsigned long long) val);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val);
+}
+
+/* this is not yet needed on the PE800, so just return 0. */
+static int ipath_pe_intconfig(struct ipath_devdata *dd)
+{
+       return 0;
+}
+
+/**
+ * ipath_setup_pe_setextled - set the state of the two external LEDs
+ * @dd: the infinipath device
+ * @lst: the L state
+ * @ltst: the LT state
+
+ * These LEDs indicate the physical and logical state of IB link.
+ * For this chip (at least with recommended board pinouts), LED1
+ * is Yellow (logical state) and LED2 is Green (physical state),
+ *
+ * Note:  We try to match the Mellanox HCA LED behavior as best
+ * we can.  Green indicates physical link state is OK (something is
+ * plugged in, and we can train).
+ * Amber indicates the link is logically up (ACTIVE).
+ * Mellanox further blinks the amber LED to indicate data packet
+ * activity, but we have no hardware support for that, so it would
+ * require waking up every 10-20 msecs and checking the counters
+ * on the chip, and then turning the LED off if appropriate.  That's
+ * visible overhead, so not something we will do.
+ *
+ */
+static void ipath_setup_pe_setextled(struct ipath_devdata *dd, u64 lst,
+                                    u64 ltst)
+{
+       u64 extctl;
+
+       /* the diags use the LED to indicate diag info, so we leave
+        * the external LED alone when the diags are running */
+       if (ipath_diag_inuse)
+               return;
+
+       extctl = dd->ipath_extctrl & ~(INFINIPATH_EXTC_LED1PRIPORT_ON |
+                                      INFINIPATH_EXTC_LED2PRIPORT_ON);
+
+       if (ltst & INFINIPATH_IBCS_LT_STATE_LINKUP)
+               extctl |= INFINIPATH_EXTC_LED2PRIPORT_ON;
+       if (lst == INFINIPATH_IBCS_L_STATE_ACTIVE)
+               extctl |= INFINIPATH_EXTC_LED1PRIPORT_ON;
+       dd->ipath_extctrl = extctl;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl);
+}
+
+/**
+ * ipath_setup_pe_cleanup - clean up any per-chip chip-specific stuff
+ * @dd: the infinipath device
+ *
+ * This is called during driver unload.
+ * We do the pci_disable_msi here, not in generic code, because it
+ * isn't used for the HT-400. If we do end up needing pci_enable_msi
+ * at some point in the future for HT-400, we'll move the call back
+ * into the main init_one code.
+ */
+static void ipath_setup_pe_cleanup(struct ipath_devdata *dd)
+{
+       dd->ipath_msi_lo = 0;   /* just in case unload fails */
+       pci_disable_msi(dd->pcidev);
+}
+
+/**
+ * ipath_setup_pe_config - setup PCIe config related stuff
+ * @dd: the infinipath device
+ * @pdev: the PCI device
+ *
+ * The pci_enable_msi() call will fail on systems with MSI quirks
+ * such as those with AMD8131, even if the device of interest is not
+ * attached to that device, (in the 2.6.13 - 2.6.15 kernels, at least, fixed
+ * late in 2.6.16).
+ * All that can be done is to edit the kernel source to remove the quirk
+ * check until that is fixed.
+ * We do not need to call enable_msi() for our HyperTransport chip (HT-400),
+ * even those it uses MSI, and we want to avoid the quirk warning, so
+ * So we call enable_msi only for the PE-800.  If we do end up needing
+ * pci_enable_msi at some point in the future for HT-400, we'll move the
+ * call back into the main init_one code.
+ * We save the msi lo and hi values, so we can restore them after
+ * chip reset (the kernel PCI infrastructure doesn't yet handle that
+ * correctly).
+ */
+static int ipath_setup_pe_config(struct ipath_devdata *dd,
+                                struct pci_dev *pdev)
+{
+       int pos, ret;
+
+       dd->ipath_msi_lo = 0;   /* used as a flag during reset processing */
+       ret = pci_enable_msi(dd->pcidev);
+       if (ret)
+               ipath_dev_err(dd, "pci_enable_msi failed: %d, "
+                             "interrupts may not work\n", ret);
+       /* continue even if it fails, we may still be OK... */
+
+       if ((pos = pci_find_capability(dd->pcidev, PCI_CAP_ID_MSI))) {
+               u16 control;
+               pci_read_config_dword(dd->pcidev, pos + PCI_MSI_ADDRESS_LO,
+                                     &dd->ipath_msi_lo);
+               pci_read_config_dword(dd->pcidev, pos + PCI_MSI_ADDRESS_HI,
+                                     &dd->ipath_msi_hi);
+               pci_read_config_word(dd->pcidev, pos + PCI_MSI_FLAGS,
+                                    &control);
+               /* now save the data (vector) info */
+               pci_read_config_word(dd->pcidev,
+                                    pos + ((control & PCI_MSI_FLAGS_64BIT)
+                                           ? 12 : 8),
+                                    &dd->ipath_msi_data);
+               ipath_cdbg(VERBOSE, "Read msi data 0x%x from config offset "
+                          "0x%x, control=0x%x\n", dd->ipath_msi_data,
+                          pos + ((control & PCI_MSI_FLAGS_64BIT) ? 12 : 8),
+                          control);
+               /* we save the cachelinesize also, although it doesn't
+                * really matter */
+               pci_read_config_byte(dd->pcidev, PCI_CACHE_LINE_SIZE,
+                                    &dd->ipath_pci_cacheline);
+       } else
+               ipath_dev_err(dd, "Can't find MSI capability, "
+                             "can't save MSI settings for reset\n");
+       if ((pos = pci_find_capability(dd->pcidev, PCI_CAP_ID_EXP))) {
+               u16 linkstat;
+               pci_read_config_word(dd->pcidev, pos + PCI_EXP_LNKSTA,
+                                    &linkstat);
+               linkstat >>= 4;
+               linkstat &= 0x1f;
+               if (linkstat != 8)
+                       ipath_dev_err(dd, "PCIe width %u, "
+                                     "performance reduced\n", linkstat);
+       }
+       else
+               ipath_dev_err(dd, "Can't find PCI Express "
+                             "capability!\n");
+       return 0;
+}
+
+static void ipath_init_pe_variables(void)
+{
+       /*
+        * bits for selecting i2c direction and values,
+        * used for I2C serial flash
+        */
+       ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
+       ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
+       ipath_gpio_sda = IPATH_GPIO_SDA;
+       ipath_gpio_scl = IPATH_GPIO_SCL;
+
+       /* variables for sanity checking interrupt and errors */
+       infinipath_hwe_bitsextant =
+               (INFINIPATH_HWE_RXEMEMPARITYERR_MASK <<
+                INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) |
+               (INFINIPATH_HWE_PCIEMEMPARITYERR_MASK <<
+                INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT) |
+               INFINIPATH_HWE_PCIE1PLLFAILED |
+               INFINIPATH_HWE_PCIE0PLLFAILED |
+               INFINIPATH_HWE_PCIEPOISONEDTLP |
+               INFINIPATH_HWE_PCIECPLTIMEOUT |
+               INFINIPATH_HWE_PCIEBUSPARITYXTLH |
+               INFINIPATH_HWE_PCIEBUSPARITYXADM |
+               INFINIPATH_HWE_PCIEBUSPARITYRADM |
+               INFINIPATH_HWE_MEMBISTFAILED |
+               INFINIPATH_HWE_COREPLL_FBSLIP |
+               INFINIPATH_HWE_COREPLL_RFSLIP |
+               INFINIPATH_HWE_SERDESPLLFAILED |
+               INFINIPATH_HWE_IBCBUSTOSPCPARITYERR |
+               INFINIPATH_HWE_IBCBUSFRSPCPARITYERR;
+       infinipath_i_bitsextant =
+               (INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) |
+               (INFINIPATH_I_RCVAVAIL_MASK <<
+                INFINIPATH_I_RCVAVAIL_SHIFT) |
+               INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT |
+               INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO;
+       infinipath_e_bitsextant =
+               INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC |
+               INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN |
+               INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN |
+               INFINIPATH_E_RSHORTPKTLEN | INFINIPATH_E_RUNEXPCHAR |
+               INFINIPATH_E_RUNSUPVL | INFINIPATH_E_REBP |
+               INFINIPATH_E_RIBFLOW | INFINIPATH_E_RBADVERSION |
+               INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL |
+               INFINIPATH_E_RBADTID | INFINIPATH_E_RHDRLEN |
+               INFINIPATH_E_RHDR | INFINIPATH_E_RIBLOSTLINK |
+               INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SMAXPKTLEN |
+               INFINIPATH_E_SUNDERRUN | INFINIPATH_E_SPKTLEN |
+               INFINIPATH_E_SDROPPEDSMPPKT | INFINIPATH_E_SDROPPEDDATAPKT |
+               INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SUNEXPERRPKTNUM |
+               INFINIPATH_E_SUNSUPVL | INFINIPATH_E_IBSTATUSCHANGED |
+               INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET |
+               INFINIPATH_E_HARDWARE;
+
+       infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
+       infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
+}
+
+/* setup the MSI stuff again after a reset.  I'd like to just call
+ * pci_enable_msi() and request_irq() again, but when I do that,
+ * the MSI enable bit doesn't get set in the command word, and
+ * we switch to to a different interrupt vector, which is confusing,
+ * so I instead just do it all inline.  Perhaps somehow can tie this
+ * into the PCIe hotplug support at some point
+ * Note, because I'm doing it all here, I don't call pci_disable_msi()
+ * or free_irq() at the start of ipath_setup_pe_reset().
+ */
+static int ipath_reinit_msi(struct ipath_devdata *dd)
+{
+       int pos;
+       u16 control;
+       int ret;
+
+       if (!dd->ipath_msi_lo) {
+               dev_info(&dd->pcidev->dev, "Can't restore MSI config, "
+                        "initial setup failed?\n");
+               ret = 0;
+               goto bail;
+       }
+
+       if (!(pos = pci_find_capability(dd->pcidev, PCI_CAP_ID_MSI))) {
+               ipath_dev_err(dd, "Can't find MSI capability, "
+                             "can't restore MSI settings\n");
+               ret = 0;
+               goto bail;
+       }
+       ipath_cdbg(VERBOSE, "Writing msi_lo 0x%x to config offset 0x%x\n",
+                  dd->ipath_msi_lo, pos + PCI_MSI_ADDRESS_LO);
+       pci_write_config_dword(dd->pcidev, pos + PCI_MSI_ADDRESS_LO,
+                              dd->ipath_msi_lo);
+       ipath_cdbg(VERBOSE, "Writing msi_lo 0x%x to config offset 0x%x\n",
+                  dd->ipath_msi_hi, pos + PCI_MSI_ADDRESS_HI);
+       pci_write_config_dword(dd->pcidev, pos + PCI_MSI_ADDRESS_HI,
+                              dd->ipath_msi_hi);
+       pci_read_config_word(dd->pcidev, pos + PCI_MSI_FLAGS, &control);
+       if (!(control & PCI_MSI_FLAGS_ENABLE)) {
+               ipath_cdbg(VERBOSE, "MSI control at off %x was %x, "
+                          "setting MSI enable (%x)\n", pos + PCI_MSI_FLAGS,
+                          control, control | PCI_MSI_FLAGS_ENABLE);
+               control |= PCI_MSI_FLAGS_ENABLE;
+               pci_write_config_word(dd->pcidev, pos + PCI_MSI_FLAGS,
+                                     control);
+       }
+       /* now rewrite the data (vector) info */
+       pci_write_config_word(dd->pcidev, pos +
+                             ((control & PCI_MSI_FLAGS_64BIT) ? 12 : 8),
+                             dd->ipath_msi_data);
+       /* we restore the cachelinesize also, although it doesn't really
+        * matter */
+       pci_write_config_byte(dd->pcidev, PCI_CACHE_LINE_SIZE,
+                             dd->ipath_pci_cacheline);
+       /* and now set the pci master bit again */
+       pci_set_master(dd->pcidev);
+       ret = 1;
+
+bail:
+       return ret;
+}
+
+/* This routine sleeps, so it can only be called from user context, not
+ * from interrupt context.  If we need interrupt context, we can split
+ * it into two routines.
+*/
+static int ipath_setup_pe_reset(struct ipath_devdata *dd)
+{
+       u64 val;
+       int i;
+       int ret;
+
+       /* Use ERROR so it shows up in logs, etc. */
+       ipath_dev_err(dd, "Resetting PE-800 unit %u\n",
+                     dd->ipath_unit);
+       val = dd->ipath_control | INFINIPATH_C_RESET;
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_control, val);
+       mb();
+
+       for (i = 1; i <= 5; i++) {
+               int r;
+               /* allow MBIST, etc. to complete; longer on each retry.
+                * We sometimes get machine checks from bus timeout if no
+                * response, so for now, make it *really* long.
+                */
+               msleep(1000 + (1 + i) * 2000);
+               if ((r =
+                    pci_write_config_dword(dd->pcidev, PCI_BASE_ADDRESS_0,
+                                           dd->ipath_pcibar0)))
+                       ipath_dev_err(dd, "rewrite of BAR0 failed: %d\n",
+                                     r);
+               if ((r =
+                    pci_write_config_dword(dd->pcidev, PCI_BASE_ADDRESS_1,
+                                           dd->ipath_pcibar1)))
+                       ipath_dev_err(dd, "rewrite of BAR1 failed: %d\n",
+                                     r);
+               /* now re-enable memory access */
+               if ((r = pci_enable_device(dd->pcidev)))
+                       ipath_dev_err(dd, "pci_enable_device failed after "
+                                     "reset: %d\n", r);
+               val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_revision);
+               if (val == dd->ipath_revision) {
+                       ipath_cdbg(VERBOSE, "Got matching revision "
+                                  "register %llx on try %d\n",
+                                  (unsigned long long) val, i);
+                       ret = ipath_reinit_msi(dd);
+                       goto bail;
+               }
+               /* Probably getting -1 back */
+               ipath_dbg("Didn't get expected revision register, "
+                         "got %llx, try %d\n", (unsigned long long) val,
+                         i + 1);
+       }
+       ret = 0; /* failed */
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_pe_put_tid - write a TID in chip
+ * @dd: the infinipath device
+ * @tidptr: pointer to the expected TID (in chip) to udpate
+ * @tidtype: 0 for eager, 1 for expected
+ * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing
+ *
+ * This exists as a separate routine to allow for special locking etc.
+ * It's used for both the full cleanup on exit, as well as the normal
+ * setup and teardown.
+ */
+static void ipath_pe_put_tid(struct ipath_devdata *dd, u64 __iomem *tidptr,
+                            u32 type, unsigned long pa)
+{
+       u32 __iomem *tidp32 = (u32 __iomem *)tidptr;
+       unsigned long flags = 0; /* keep gcc quiet */
+
+       if (pa != dd->ipath_tidinvalid) {
+               if (pa & ((1U << 11) - 1)) {
+                       dev_info(&dd->pcidev->dev, "BUG: physaddr %lx "
+                                "not 4KB aligned!\n", pa);
+                       return;
+               }
+               pa >>= 11;
+               /* paranoia check */
+               if (pa & (7<<29))
+                       ipath_dev_err(dd,
+                                     "BUG: Physical page address 0x%lx "
+                                     "has bits set in 31-29\n", pa);
+
+               if (type == 0)
+                       pa |= dd->ipath_tidtemplate;
+               else /* for now, always full 4KB page */
+                       pa |= 2 << 29;
+       }
+
+       /* workaround chip bug 9437 by writing each TID twice
+        * and holding a spinlock around the writes, so they don't
+        * intermix with other TID (eager or expected) writes
+        * Unfortunately, this call can be done from interrupt level
+        * for the port 0 eager TIDs, so we have to use irqsave
+        */
+       spin_lock_irqsave(&dd->ipath_tid_lock, flags);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_scratch, 0xfeeddeaf);
+       if (dd->ipath_kregbase)
+               writel(pa, tidp32);
+       ipath_write_kreg(dd, dd->ipath_kregs->kr_scratch, 0xdeadbeef);
+       mmiowb();
+       spin_unlock_irqrestore(&dd->ipath_tid_lock, flags);
+}
+
+/**
+ * ipath_pe_clear_tid - clear all TID entries for a port, expected and eager
+ * @dd: the infinipath device
+ * @port: the port
+ *
+ * clear all TID entries for a port, expected and eager.
+ * Used from ipath_close().  On PE800, TIDs are only 32 bits,
+ * not 64, but they are still on 64 bit boundaries, so tidbase
+ * is declared as u64 * for the pointer math, even though we write 32 bits
+ */
+static void ipath_pe_clear_tids(struct ipath_devdata *dd, unsigned port)
+{
+       u64 __iomem *tidbase;
+       unsigned long tidinv;
+       int i;
+
+       if (!dd->ipath_kregbase)
+               return;
+
+       ipath_cdbg(VERBOSE, "Invalidate TIDs for port %u\n", port);
+
+       tidinv = dd->ipath_tidinvalid;
+       tidbase = (u64 __iomem *)
+               ((char __iomem *)(dd->ipath_kregbase) +
+                dd->ipath_rcvtidbase +
+                port * dd->ipath_rcvtidcnt * sizeof(*tidbase));
+
+       for (i = 0; i < dd->ipath_rcvtidcnt; i++)
+               ipath_pe_put_tid(dd, &tidbase[i], 0, tidinv);
+
+       tidbase = (u64 __iomem *)
+               ((char __iomem *)(dd->ipath_kregbase) +
+                dd->ipath_rcvegrbase +
+                port * dd->ipath_rcvegrcnt * sizeof(*tidbase));
+
+       for (i = 0; i < dd->ipath_rcvegrcnt; i++)
+               ipath_pe_put_tid(dd, &tidbase[i], 1, tidinv);
+}
+
+/**
+ * ipath_pe_tidtemplate - setup constants for TID updates
+ * @dd: the infinipath device
+ *
+ * We setup stuff that we use a lot, to avoid calculating each time
+ */
+static void ipath_pe_tidtemplate(struct ipath_devdata *dd)
+{
+       u32 egrsize = dd->ipath_rcvegrbufsize;
+
+       /* For now, we always allocate 4KB buffers (at init) so we can
+        * receive max size packets.  We may want a module parameter to
+        * specify 2KB or 4KB and/or make be per port instead of per device
+        * for those who want to reduce memory footprint.  Note that the
+        * ipath_rcvhdrentsize size must be large enough to hold the largest
+        * IB header (currently 96 bytes) that we expect to handle (plus of
+        * course the 2 dwords of RHF).
+        */
+       if (egrsize == 2048)
+               dd->ipath_tidtemplate = 1U << 29;
+       else if (egrsize == 4096)
+               dd->ipath_tidtemplate = 2U << 29;
+       else {
+               egrsize = 4096;
+               dev_info(&dd->pcidev->dev, "BUG: unsupported egrbufsize "
+                        "%u, using %u\n", dd->ipath_rcvegrbufsize,
+                        egrsize);
+               dd->ipath_tidtemplate = 2U << 29;
+       }
+       dd->ipath_tidinvalid = 0;
+}
+
+static int ipath_pe_early_init(struct ipath_devdata *dd)
+{
+       dd->ipath_flags |= IPATH_4BYTE_TID;
+
+       /*
+        * For openib, we need to be able to handle an IB header of 96 bytes
+        * or 24 dwords.  HT-400 has arbitrary sized receive buffers, so we
+        * made them the same size as the PIO buffers.  The PE-800 does not
+        * handle arbitrary size buffers, so we need the header large enough
+        * to handle largest IB header, but still have room for a 2KB MTU
+        * standard IB packet.
+        */
+       dd->ipath_rcvhdrentsize = 24;
+       dd->ipath_rcvhdrsize = IPATH_DFLT_RCVHDRSIZE;
+
+       /* For HT-400, we allocate a somewhat overly large eager buffer,
+        * such that we can guarantee that we can receive the largest packet
+        * that we can send out.  To truly support a 4KB MTU, we need to
+        * bump this to a larger value.  We'll do this when I get around to
+        * testing 4KB sends on the PE-800, which I have not yet done.
+        */
+       dd->ipath_rcvegrbufsize = 2048;
+       /*
+        * the min() check here is currently a nop, but it may not always
+        * be, depending on just how we do ipath_rcvegrbufsize
+        */
+       dd->ipath_ibmaxlen = min(dd->ipath_piosize2k,
+                                dd->ipath_rcvegrbufsize +
+                                (dd->ipath_rcvhdrentsize << 2));
+       dd->ipath_init_ibmaxlen = dd->ipath_ibmaxlen;
+
+       /*
+        * For PE-800, we can request a receive interrupt for 1 or
+        * more packets from current offset.  For now, we set this
+        * up for a single packet, to match the HT-400 behavior.
+        */
+       dd->ipath_rhdrhead_intr_off = 1ULL<<32;
+
+       return 0;
+}
+
+int __attribute__((weak)) ipath_unordered_wc(void)
+{
+       return 0;
+}
+
+/**
+ * ipath_init_pe_get_base_info - set chip-specific flags for user code
+ * @dd: the infinipath device
+ * @kbase: ipath_base_info pointer
+ *
+ * We set the PCIE flag because the lower bandwidth on PCIe vs
+ * HyperTransport can affect some user packet algorithims.
+ */
+static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase)
+{
+       struct ipath_base_info *kinfo = kbase;
+
+       if (ipath_unordered_wc()) {
+               kinfo->spi_runtime_flags |= IPATH_RUNTIME_FORCE_WC_ORDER;
+               ipath_cdbg(PROC, "Intel processor, forcing WC order\n");
+       }
+       else
+               ipath_cdbg(PROC, "Not Intel processor, WC ordered\n");
+
+       kinfo->spi_runtime_flags |= IPATH_RUNTIME_PCIE;
+
+       return 0;
+}
+
+/**
+ * ipath_init_pe800_funcs - set up the chip-specific function pointers
+ * @dd: the infinipath device
+ *
+ * This is global, and is called directly at init to set up the
+ * chip-specific function pointers for later use.
+ */
+void ipath_init_pe800_funcs(struct ipath_devdata *dd)
+{
+       dd->ipath_f_intrsetup = ipath_pe_intconfig;
+       dd->ipath_f_bus = ipath_setup_pe_config;
+       dd->ipath_f_reset = ipath_setup_pe_reset;
+       dd->ipath_f_get_boardname = ipath_pe_boardname;
+       dd->ipath_f_init_hwerrors = ipath_pe_init_hwerrors;
+       dd->ipath_f_early_init = ipath_pe_early_init;
+       dd->ipath_f_handle_hwerrors = ipath_pe_handle_hwerrors;
+       dd->ipath_f_quiet_serdes = ipath_pe_quiet_serdes;
+       dd->ipath_f_bringup_serdes = ipath_pe_bringup_serdes;
+       dd->ipath_f_clear_tids = ipath_pe_clear_tids;
+       dd->ipath_f_put_tid = ipath_pe_put_tid;
+       dd->ipath_f_cleanup = ipath_setup_pe_cleanup;
+       dd->ipath_f_setextled = ipath_setup_pe_setextled;
+       dd->ipath_f_get_base_info = ipath_pe_get_base_info;
+
+       /* initialize chip-specific variables */
+       dd->ipath_f_tidtemplate = ipath_pe_tidtemplate;
+
+       /*
+        * setup the register offsets, since they are different for each
+        * chip
+        */
+       dd->ipath_kregs = &ipath_pe_kregs;
+       dd->ipath_cregs = &ipath_pe_cregs;
+
+       ipath_init_pe_variables();
+}
+
diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c
new file mode 100644 (file)
index 0000000..6058d70
--- /dev/null
@@ -0,0 +1,913 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/err.h>
+#include <linux/vmalloc.h>
+
+#include "ipath_verbs.h"
+#include "ips_common.h"
+
+#define BITS_PER_PAGE          (PAGE_SIZE*BITS_PER_BYTE)
+#define BITS_PER_PAGE_MASK     (BITS_PER_PAGE-1)
+#define mk_qpn(qpt, map, off)  (((map) - (qpt)->map) * BITS_PER_PAGE + \
+                                (off))
+#define find_next_offset(map, off) find_next_zero_bit((map)->page, \
+                                                     BITS_PER_PAGE, off)
+
+#define TRANS_INVALID  0
+#define TRANS_ANY2RST  1
+#define TRANS_RST2INIT 2
+#define TRANS_INIT2INIT        3
+#define TRANS_INIT2RTR 4
+#define TRANS_RTR2RTS  5
+#define TRANS_RTS2RTS  6
+#define TRANS_SQERR2RTS        7
+#define TRANS_ANY2ERR  8
+#define TRANS_RTS2SQD  9  /* XXX Wait for expected ACKs & signal event */
+#define TRANS_SQD2SQD  10 /* error if not drained & parameter change */
+#define TRANS_SQD2RTS  11 /* error if not drained */
+
+/*
+ * Convert the AETH credit code into the number of credits.
+ */
+static u32 credit_table[31] = {
+       0,                      /* 0 */
+       1,                      /* 1 */
+       2,                      /* 2 */
+       3,                      /* 3 */
+       4,                      /* 4 */
+       6,                      /* 5 */
+       8,                      /* 6 */
+       12,                     /* 7 */
+       16,                     /* 8 */
+       24,                     /* 9 */
+       32,                     /* A */
+       48,                     /* B */
+       64,                     /* C */
+       96,                     /* D */
+       128,                    /* E */
+       192,                    /* F */
+       256,                    /* 10 */
+       384,                    /* 11 */
+       512,                    /* 12 */
+       768,                    /* 13 */
+       1024,                   /* 14 */
+       1536,                   /* 15 */
+       2048,                   /* 16 */
+       3072,                   /* 17 */
+       4096,                   /* 18 */
+       6144,                   /* 19 */
+       8192,                   /* 1A */
+       12288,                  /* 1B */
+       16384,                  /* 1C */
+       24576,                  /* 1D */
+       32768                   /* 1E */
+};
+
+static u32 alloc_qpn(struct ipath_qp_table *qpt)
+{
+       u32 i, offset, max_scan, qpn;
+       struct qpn_map *map;
+       u32 ret;
+
+       qpn = qpt->last + 1;
+       if (qpn >= QPN_MAX)
+               qpn = 2;
+       offset = qpn & BITS_PER_PAGE_MASK;
+       map = &qpt->map[qpn / BITS_PER_PAGE];
+       max_scan = qpt->nmaps - !offset;
+       for (i = 0;;) {
+               if (unlikely(!map->page)) {
+                       unsigned long page = get_zeroed_page(GFP_KERNEL);
+                       unsigned long flags;
+
+                       /*
+                        * Free the page if someone raced with us
+                        * installing it:
+                        */
+                       spin_lock_irqsave(&qpt->lock, flags);
+                       if (map->page)
+                               free_page(page);
+                       else
+                               map->page = (void *)page;
+                       spin_unlock_irqrestore(&qpt->lock, flags);
+                       if (unlikely(!map->page))
+                               break;
+               }
+               if (likely(atomic_read(&map->n_free))) {
+                       do {
+                               if (!test_and_set_bit(offset, map->page)) {
+                                       atomic_dec(&map->n_free);
+                                       qpt->last = qpn;
+                                       ret = qpn;
+                                       goto bail;
+                               }
+                               offset = find_next_offset(map, offset);
+                               qpn = mk_qpn(qpt, map, offset);
+                               /*
+                                * This test differs from alloc_pidmap().
+                                * If find_next_offset() does find a zero
+                                * bit, we don't need to check for QPN
+                                * wrapping around past our starting QPN.
+                                * We just need to be sure we don't loop
+                                * forever.
+                                */
+                       } while (offset < BITS_PER_PAGE && qpn < QPN_MAX);
+               }
+               /*
+                * In order to keep the number of pages allocated to a
+                * minimum, we scan the all existing pages before increasing
+                * the size of the bitmap table.
+                */
+               if (++i > max_scan) {
+                       if (qpt->nmaps == QPNMAP_ENTRIES)
+                               break;
+                       map = &qpt->map[qpt->nmaps++];
+                       offset = 0;
+               } else if (map < &qpt->map[qpt->nmaps]) {
+                       ++map;
+                       offset = 0;
+               } else {
+                       map = &qpt->map[0];
+                       offset = 2;
+               }
+               qpn = mk_qpn(qpt, map, offset);
+       }
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+static void free_qpn(struct ipath_qp_table *qpt, u32 qpn)
+{
+       struct qpn_map *map;
+
+       map = qpt->map + qpn / BITS_PER_PAGE;
+       if (map->page)
+               clear_bit(qpn & BITS_PER_PAGE_MASK, map->page);
+       atomic_inc(&map->n_free);
+}
+
+/**
+ * ipath_alloc_qpn - allocate a QP number
+ * @qpt: the QP table
+ * @qp: the QP
+ * @type: the QP type (IB_QPT_SMI and IB_QPT_GSI are special)
+ *
+ * Allocate the next available QPN and put the QP into the hash table.
+ * The hash table holds a reference to the QP.
+ */
+int ipath_alloc_qpn(struct ipath_qp_table *qpt, struct ipath_qp *qp,
+                   enum ib_qp_type type)
+{
+       unsigned long flags;
+       u32 qpn;
+       int ret;
+
+       if (type == IB_QPT_SMI)
+               qpn = 0;
+       else if (type == IB_QPT_GSI)
+               qpn = 1;
+       else {
+               /* Allocate the next available QPN */
+               qpn = alloc_qpn(qpt);
+               if (qpn == 0) {
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+       }
+       qp->ibqp.qp_num = qpn;
+
+       /* Add the QP to the hash table. */
+       spin_lock_irqsave(&qpt->lock, flags);
+
+       qpn %= qpt->max;
+       qp->next = qpt->table[qpn];
+       qpt->table[qpn] = qp;
+       atomic_inc(&qp->refcount);
+
+       spin_unlock_irqrestore(&qpt->lock, flags);
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_free_qp - remove a QP from the QP table
+ * @qpt: the QP table
+ * @qp: the QP to remove
+ *
+ * Remove the QP from the table so it can't be found asynchronously by
+ * the receive interrupt routine.
+ */
+void ipath_free_qp(struct ipath_qp_table *qpt, struct ipath_qp *qp)
+{
+       struct ipath_qp *q, **qpp;
+       unsigned long flags;
+       int fnd = 0;
+
+       spin_lock_irqsave(&qpt->lock, flags);
+
+       /* Remove QP from the hash table. */
+       qpp = &qpt->table[qp->ibqp.qp_num % qpt->max];
+       for (; (q = *qpp) != NULL; qpp = &q->next) {
+               if (q == qp) {
+                       *qpp = qp->next;
+                       qp->next = NULL;
+                       atomic_dec(&qp->refcount);
+                       fnd = 1;
+                       break;
+               }
+       }
+
+       spin_unlock_irqrestore(&qpt->lock, flags);
+
+       if (!fnd)
+               return;
+
+       /* If QPN is not reserved, mark QPN free in the bitmap. */
+       if (qp->ibqp.qp_num > 1)
+               free_qpn(qpt, qp->ibqp.qp_num);
+
+       wait_event(qp->wait, !atomic_read(&qp->refcount));
+}
+
+/**
+ * ipath_free_all_qps - remove all QPs from the table
+ * @qpt: the QP table to empty
+ */
+void ipath_free_all_qps(struct ipath_qp_table *qpt)
+{
+       unsigned long flags;
+       struct ipath_qp *qp, *nqp;
+       u32 n;
+
+       for (n = 0; n < qpt->max; n++) {
+               spin_lock_irqsave(&qpt->lock, flags);
+               qp = qpt->table[n];
+               qpt->table[n] = NULL;
+               spin_unlock_irqrestore(&qpt->lock, flags);
+
+               while (qp) {
+                       nqp = qp->next;
+                       if (qp->ibqp.qp_num > 1)
+                               free_qpn(qpt, qp->ibqp.qp_num);
+                       if (!atomic_dec_and_test(&qp->refcount) ||
+                           !ipath_destroy_qp(&qp->ibqp))
+                               _VERBS_INFO("QP memory leak!\n");
+                       qp = nqp;
+               }
+       }
+
+       for (n = 0; n < ARRAY_SIZE(qpt->map); n++) {
+               if (qpt->map[n].page)
+                       free_page((unsigned long)qpt->map[n].page);
+       }
+}
+
+/**
+ * ipath_lookup_qpn - return the QP with the given QPN
+ * @qpt: the QP table
+ * @qpn: the QP number to look up
+ *
+ * The caller is responsible for decrementing the QP reference count
+ * when done.
+ */
+struct ipath_qp *ipath_lookup_qpn(struct ipath_qp_table *qpt, u32 qpn)
+{
+       unsigned long flags;
+       struct ipath_qp *qp;
+
+       spin_lock_irqsave(&qpt->lock, flags);
+
+       for (qp = qpt->table[qpn % qpt->max]; qp; qp = qp->next) {
+               if (qp->ibqp.qp_num == qpn) {
+                       atomic_inc(&qp->refcount);
+                       break;
+               }
+       }
+
+       spin_unlock_irqrestore(&qpt->lock, flags);
+       return qp;
+}
+
+/**
+ * ipath_reset_qp - initialize the QP state to the reset state
+ * @qp: the QP to reset
+ */
+static void ipath_reset_qp(struct ipath_qp *qp)
+{
+       qp->remote_qpn = 0;
+       qp->qkey = 0;
+       qp->qp_access_flags = 0;
+       qp->s_hdrwords = 0;
+       qp->s_psn = 0;
+       qp->r_psn = 0;
+       atomic_set(&qp->msn, 0);
+       if (qp->ibqp.qp_type == IB_QPT_RC) {
+               qp->s_state = IB_OPCODE_RC_SEND_LAST;
+               qp->r_state = IB_OPCODE_RC_SEND_LAST;
+       } else {
+               qp->s_state = IB_OPCODE_UC_SEND_LAST;
+               qp->r_state = IB_OPCODE_UC_SEND_LAST;
+       }
+       qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE;
+       qp->s_nak_state = 0;
+       qp->s_rnr_timeout = 0;
+       qp->s_head = 0;
+       qp->s_tail = 0;
+       qp->s_cur = 0;
+       qp->s_last = 0;
+       qp->s_ssn = 1;
+       qp->s_lsn = 0;
+       qp->r_rq.head = 0;
+       qp->r_rq.tail = 0;
+       qp->r_reuse_sge = 0;
+}
+
+/**
+ * ipath_modify_qp - modify the attributes of a queue pair
+ * @ibqp: the queue pair who's attributes we're modifying
+ * @attr: the new attributes
+ * @attr_mask: the mask of attributes to modify
+ *
+ * Returns 0 on success, otherwise returns an errno.
+ */
+int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+                   int attr_mask)
+{
+       struct ipath_qp *qp = to_iqp(ibqp);
+       enum ib_qp_state cur_state, new_state;
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&qp->r_rq.lock, flags);
+       spin_lock(&qp->s_lock);
+
+       cur_state = attr_mask & IB_QP_CUR_STATE ?
+               attr->cur_qp_state : qp->state;
+       new_state = attr_mask & IB_QP_STATE ? attr->qp_state : cur_state;
+
+       if (!ib_modify_qp_is_ok(cur_state, new_state, ibqp->qp_type,
+                               attr_mask))
+               goto inval;
+
+       switch (new_state) {
+       case IB_QPS_RESET:
+               ipath_reset_qp(qp);
+               break;
+
+       case IB_QPS_ERR:
+               ipath_error_qp(qp);
+               break;
+
+       default:
+               break;
+
+       }
+
+       if (attr_mask & IB_QP_PKEY_INDEX) {
+               struct ipath_ibdev *dev = to_idev(ibqp->device);
+
+               if (attr->pkey_index >= ipath_layer_get_npkeys(dev->dd))
+                       goto inval;
+               qp->s_pkey_index = attr->pkey_index;
+       }
+
+       if (attr_mask & IB_QP_DEST_QPN)
+               qp->remote_qpn = attr->dest_qp_num;
+
+       if (attr_mask & IB_QP_SQ_PSN) {
+               qp->s_next_psn = attr->sq_psn;
+               qp->s_last_psn = qp->s_next_psn - 1;
+       }
+
+       if (attr_mask & IB_QP_RQ_PSN)
+               qp->r_psn = attr->rq_psn;
+
+       if (attr_mask & IB_QP_ACCESS_FLAGS)
+               qp->qp_access_flags = attr->qp_access_flags;
+
+       if (attr_mask & IB_QP_AV) {
+               if (attr->ah_attr.dlid == 0 ||
+                   attr->ah_attr.dlid >= IPS_MULTICAST_LID_BASE)
+                       goto inval;
+               qp->remote_ah_attr = attr->ah_attr;
+       }
+
+       if (attr_mask & IB_QP_PATH_MTU)
+               qp->path_mtu = attr->path_mtu;
+
+       if (attr_mask & IB_QP_RETRY_CNT)
+               qp->s_retry = qp->s_retry_cnt = attr->retry_cnt;
+
+       if (attr_mask & IB_QP_RNR_RETRY) {
+               qp->s_rnr_retry = attr->rnr_retry;
+               if (qp->s_rnr_retry > 7)
+                       qp->s_rnr_retry = 7;
+               qp->s_rnr_retry_cnt = qp->s_rnr_retry;
+       }
+
+       if (attr_mask & IB_QP_MIN_RNR_TIMER) {
+               if (attr->min_rnr_timer > 31)
+                       goto inval;
+               qp->s_min_rnr_timer = attr->min_rnr_timer;
+       }
+
+       if (attr_mask & IB_QP_QKEY)
+               qp->qkey = attr->qkey;
+
+       if (attr_mask & IB_QP_PKEY_INDEX)
+               qp->s_pkey_index = attr->pkey_index;
+
+       qp->state = new_state;
+       spin_unlock(&qp->s_lock);
+       spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+
+       /*
+        * If QP1 changed to the RTS state, try to move to the link to INIT
+        * even if it was ACTIVE so the SM will reinitialize the SMA's
+        * state.
+        */
+       if (qp->ibqp.qp_num == 1 && new_state == IB_QPS_RTS) {
+               struct ipath_ibdev *dev = to_idev(ibqp->device);
+
+               ipath_layer_set_linkstate(dev->dd, IPATH_IB_LINKDOWN);
+       }
+       ret = 0;
+       goto bail;
+
+inval:
+       spin_unlock(&qp->s_lock);
+       spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+       ret = -EINVAL;
+
+bail:
+       return ret;
+}
+
+int ipath_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+                  int attr_mask, struct ib_qp_init_attr *init_attr)
+{
+       struct ipath_qp *qp = to_iqp(ibqp);
+
+       attr->qp_state = qp->state;
+       attr->cur_qp_state = attr->qp_state;
+       attr->path_mtu = qp->path_mtu;
+       attr->path_mig_state = 0;
+       attr->qkey = qp->qkey;
+       attr->rq_psn = qp->r_psn;
+       attr->sq_psn = qp->s_next_psn;
+       attr->dest_qp_num = qp->remote_qpn;
+       attr->qp_access_flags = qp->qp_access_flags;
+       attr->cap.max_send_wr = qp->s_size - 1;
+       attr->cap.max_recv_wr = qp->r_rq.size - 1;
+       attr->cap.max_send_sge = qp->s_max_sge;
+       attr->cap.max_recv_sge = qp->r_rq.max_sge;
+       attr->cap.max_inline_data = 0;
+       attr->ah_attr = qp->remote_ah_attr;
+       memset(&attr->alt_ah_attr, 0, sizeof(attr->alt_ah_attr));
+       attr->pkey_index = qp->s_pkey_index;
+       attr->alt_pkey_index = 0;
+       attr->en_sqd_async_notify = 0;
+       attr->sq_draining = 0;
+       attr->max_rd_atomic = 1;
+       attr->max_dest_rd_atomic = 1;
+       attr->min_rnr_timer = qp->s_min_rnr_timer;
+       attr->port_num = 1;
+       attr->timeout = 0;
+       attr->retry_cnt = qp->s_retry_cnt;
+       attr->rnr_retry = qp->s_rnr_retry;
+       attr->alt_port_num = 0;
+       attr->alt_timeout = 0;
+
+       init_attr->event_handler = qp->ibqp.event_handler;
+       init_attr->qp_context = qp->ibqp.qp_context;
+       init_attr->send_cq = qp->ibqp.send_cq;
+       init_attr->recv_cq = qp->ibqp.recv_cq;
+       init_attr->srq = qp->ibqp.srq;
+       init_attr->cap = attr->cap;
+       init_attr->sq_sig_type =
+               (qp->s_flags & (1 << IPATH_S_SIGNAL_REQ_WR))
+               ? IB_SIGNAL_REQ_WR : 0;
+       init_attr->qp_type = qp->ibqp.qp_type;
+       init_attr->port_num = 1;
+       return 0;
+}
+
+/**
+ * ipath_compute_aeth - compute the AETH (syndrome + MSN)
+ * @qp: the queue pair to compute the AETH for
+ *
+ * Returns the AETH.
+ *
+ * The QP s_lock should be held.
+ */
+__be32 ipath_compute_aeth(struct ipath_qp *qp)
+{
+       u32 aeth = atomic_read(&qp->msn) & IPS_MSN_MASK;
+
+       if (qp->s_nak_state) {
+               aeth |= qp->s_nak_state << IPS_AETH_CREDIT_SHIFT;
+       } else if (qp->ibqp.srq) {
+               /*
+                * Shared receive queues don't generate credits.
+                * Set the credit field to the invalid value.
+                */
+               aeth |= IPS_AETH_CREDIT_INVAL << IPS_AETH_CREDIT_SHIFT;
+       } else {
+               u32 min, max, x;
+               u32 credits;
+
+               /*
+                * Compute the number of credits available (RWQEs).
+                * XXX Not holding the r_rq.lock here so there is a small
+                * chance that the pair of reads are not atomic.
+                */
+               credits = qp->r_rq.head - qp->r_rq.tail;
+               if ((int)credits < 0)
+                       credits += qp->r_rq.size;
+               /*
+                * Binary search the credit table to find the code to
+                * use.
+                */
+               min = 0;
+               max = 31;
+               for (;;) {
+                       x = (min + max) / 2;
+                       if (credit_table[x] == credits)
+                               break;
+                       if (credit_table[x] > credits)
+                               max = x;
+                       else if (min == x)
+                               break;
+                       else
+                               min = x;
+               }
+               aeth |= x << IPS_AETH_CREDIT_SHIFT;
+       }
+       return cpu_to_be32(aeth);
+}
+
+/**
+ * ipath_create_qp - create a queue pair for a device
+ * @ibpd: the protection domain who's device we create the queue pair for
+ * @init_attr: the attributes of the queue pair
+ * @udata: unused by InfiniPath
+ *
+ * Returns the queue pair on success, otherwise returns an errno.
+ *
+ * Called by the ib_create_qp() core verbs function.
+ */
+struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
+                             struct ib_qp_init_attr *init_attr,
+                             struct ib_udata *udata)
+{
+       struct ipath_qp *qp;
+       int err;
+       struct ipath_swqe *swq = NULL;
+       struct ipath_ibdev *dev;
+       size_t sz;
+       struct ib_qp *ret;
+
+       if (init_attr->cap.max_send_sge > 255 ||
+           init_attr->cap.max_recv_sge > 255) {
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       switch (init_attr->qp_type) {
+       case IB_QPT_UC:
+       case IB_QPT_RC:
+               sz = sizeof(struct ipath_sge) *
+                       init_attr->cap.max_send_sge +
+                       sizeof(struct ipath_swqe);
+               swq = vmalloc((init_attr->cap.max_send_wr + 1) * sz);
+               if (swq == NULL) {
+                       ret = ERR_PTR(-ENOMEM);
+                       goto bail;
+               }
+               /* FALLTHROUGH */
+       case IB_QPT_UD:
+       case IB_QPT_SMI:
+       case IB_QPT_GSI:
+               qp = kmalloc(sizeof(*qp), GFP_KERNEL);
+               if (!qp) {
+                       ret = ERR_PTR(-ENOMEM);
+                       goto bail;
+               }
+               qp->r_rq.size = init_attr->cap.max_recv_wr + 1;
+               sz = sizeof(struct ipath_sge) *
+                       init_attr->cap.max_recv_sge +
+                       sizeof(struct ipath_rwqe);
+               qp->r_rq.wq = vmalloc(qp->r_rq.size * sz);
+               if (!qp->r_rq.wq) {
+                       kfree(qp);
+                       ret = ERR_PTR(-ENOMEM);
+                       goto bail;
+               }
+
+               /*
+                * ib_create_qp() will initialize qp->ibqp
+                * except for qp->ibqp.qp_num.
+                */
+               spin_lock_init(&qp->s_lock);
+               spin_lock_init(&qp->r_rq.lock);
+               atomic_set(&qp->refcount, 0);
+               init_waitqueue_head(&qp->wait);
+               tasklet_init(&qp->s_task,
+                            init_attr->qp_type == IB_QPT_RC ?
+                            ipath_do_rc_send : ipath_do_uc_send,
+                            (unsigned long)qp);
+               qp->piowait.next = LIST_POISON1;
+               qp->piowait.prev = LIST_POISON2;
+               qp->timerwait.next = LIST_POISON1;
+               qp->timerwait.prev = LIST_POISON2;
+               qp->state = IB_QPS_RESET;
+               qp->s_wq = swq;
+               qp->s_size = init_attr->cap.max_send_wr + 1;
+               qp->s_max_sge = init_attr->cap.max_send_sge;
+               qp->r_rq.max_sge = init_attr->cap.max_recv_sge;
+               qp->s_flags = init_attr->sq_sig_type == IB_SIGNAL_REQ_WR ?
+                       1 << IPATH_S_SIGNAL_REQ_WR : 0;
+               dev = to_idev(ibpd->device);
+               err = ipath_alloc_qpn(&dev->qp_table, qp,
+                                     init_attr->qp_type);
+               if (err) {
+                       vfree(swq);
+                       vfree(qp->r_rq.wq);
+                       kfree(qp);
+                       ret = ERR_PTR(err);
+                       goto bail;
+               }
+               ipath_reset_qp(qp);
+
+               /* Tell the core driver that the kernel SMA is present. */
+               if (qp->ibqp.qp_type == IB_QPT_SMI)
+                       ipath_layer_set_verbs_flags(dev->dd,
+                                                   IPATH_VERBS_KERNEL_SMA);
+               break;
+
+       default:
+               /* Don't support raw QPs */
+               ret = ERR_PTR(-ENOSYS);
+               goto bail;
+       }
+
+       init_attr->cap.max_inline_data = 0;
+
+       ret = &qp->ibqp;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_destroy_qp - destroy a queue pair
+ * @ibqp: the queue pair to destroy
+ *
+ * Returns 0 on success.
+ *
+ * Note that this can be called while the QP is actively sending or
+ * receiving!
+ */
+int ipath_destroy_qp(struct ib_qp *ibqp)
+{
+       struct ipath_qp *qp = to_iqp(ibqp);
+       struct ipath_ibdev *dev = to_idev(ibqp->device);
+       unsigned long flags;
+
+       /* Tell the core driver that the kernel SMA is gone. */
+       if (qp->ibqp.qp_type == IB_QPT_SMI)
+               ipath_layer_set_verbs_flags(dev->dd, 0);
+
+       spin_lock_irqsave(&qp->r_rq.lock, flags);
+       spin_lock(&qp->s_lock);
+       qp->state = IB_QPS_ERR;
+       spin_unlock(&qp->s_lock);
+       spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+
+       /* Stop the sending tasklet. */
+       tasklet_kill(&qp->s_task);
+
+       /* Make sure the QP isn't on the timeout list. */
+       spin_lock_irqsave(&dev->pending_lock, flags);
+       if (qp->timerwait.next != LIST_POISON1)
+               list_del(&qp->timerwait);
+       if (qp->piowait.next != LIST_POISON1)
+               list_del(&qp->piowait);
+       spin_unlock_irqrestore(&dev->pending_lock, flags);
+
+       /*
+        * Make sure that the QP is not in the QPN table so receive
+        * interrupts will discard packets for this QP.  XXX Also remove QP
+        * from multicast table.
+        */
+       if (atomic_read(&qp->refcount) != 0)
+               ipath_free_qp(&dev->qp_table, qp);
+
+       vfree(qp->s_wq);
+       vfree(qp->r_rq.wq);
+       kfree(qp);
+       return 0;
+}
+
+/**
+ * ipath_init_qp_table - initialize the QP table for a device
+ * @idev: the device who's QP table we're initializing
+ * @size: the size of the QP table
+ *
+ * Returns 0 on success, otherwise returns an errno.
+ */
+int ipath_init_qp_table(struct ipath_ibdev *idev, int size)
+{
+       int i;
+       int ret;
+
+       idev->qp_table.last = 1;        /* QPN 0 and 1 are special. */
+       idev->qp_table.max = size;
+       idev->qp_table.nmaps = 1;
+       idev->qp_table.table = kzalloc(size * sizeof(*idev->qp_table.table),
+                                      GFP_KERNEL);
+       if (idev->qp_table.table == NULL) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(idev->qp_table.map); i++) {
+               atomic_set(&idev->qp_table.map[i].n_free, BITS_PER_PAGE);
+               idev->qp_table.map[i].page = NULL;
+       }
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_sqerror_qp - put a QP's send queue into an error state
+ * @qp: QP who's send queue will be put into an error state
+ * @wc: the WC responsible for putting the QP in this state
+ *
+ * Flushes the send work queue.
+ * The QP s_lock should be held.
+ */
+
+void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc)
+{
+       struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
+       struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last);
+
+       _VERBS_INFO("Send queue error on QP%d/%d: err: %d\n",
+                   qp->ibqp.qp_num, qp->remote_qpn, wc->status);
+
+       spin_lock(&dev->pending_lock);
+       /* XXX What if its already removed by the timeout code? */
+       if (qp->timerwait.next != LIST_POISON1)
+               list_del(&qp->timerwait);
+       if (qp->piowait.next != LIST_POISON1)
+               list_del(&qp->piowait);
+       spin_unlock(&dev->pending_lock);
+
+       ipath_cq_enter(to_icq(qp->ibqp.send_cq), wc, 1);
+       if (++qp->s_last >= qp->s_size)
+               qp->s_last = 0;
+
+       wc->status = IB_WC_WR_FLUSH_ERR;
+
+       while (qp->s_last != qp->s_head) {
+               wc->wr_id = wqe->wr.wr_id;
+               wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
+               ipath_cq_enter(to_icq(qp->ibqp.send_cq), wc, 1);
+               if (++qp->s_last >= qp->s_size)
+                       qp->s_last = 0;
+               wqe = get_swqe_ptr(qp, qp->s_last);
+       }
+       qp->s_cur = qp->s_tail = qp->s_head;
+       qp->state = IB_QPS_SQE;
+}
+
+/**
+ * ipath_error_qp - put a QP into an error state
+ * @qp: the QP to put into an error state
+ *
+ * Flushes both send and receive work queues.
+ * QP r_rq.lock and s_lock should be held.
+ */
+
+void ipath_error_qp(struct ipath_qp *qp)
+{
+       struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
+       struct ib_wc wc;
+
+       _VERBS_INFO("QP%d/%d in error state\n",
+                   qp->ibqp.qp_num, qp->remote_qpn);
+
+       spin_lock(&dev->pending_lock);
+       /* XXX What if its already removed by the timeout code? */
+       if (qp->timerwait.next != LIST_POISON1)
+               list_del(&qp->timerwait);
+       if (qp->piowait.next != LIST_POISON1)
+               list_del(&qp->piowait);
+       spin_unlock(&dev->pending_lock);
+
+       wc.status = IB_WC_WR_FLUSH_ERR;
+       wc.vendor_err = 0;
+       wc.byte_len = 0;
+       wc.imm_data = 0;
+       wc.qp_num = qp->ibqp.qp_num;
+       wc.src_qp = 0;
+       wc.wc_flags = 0;
+       wc.pkey_index = 0;
+       wc.slid = 0;
+       wc.sl = 0;
+       wc.dlid_path_bits = 0;
+       wc.port_num = 0;
+
+       while (qp->s_last != qp->s_head) {
+               struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last);
+
+               wc.wr_id = wqe->wr.wr_id;
+               wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
+               if (++qp->s_last >= qp->s_size)
+                       qp->s_last = 0;
+               ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 1);
+       }
+       qp->s_cur = qp->s_tail = qp->s_head;
+       qp->s_hdrwords = 0;
+       qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE;
+
+       wc.opcode = IB_WC_RECV;
+       while (qp->r_rq.tail != qp->r_rq.head) {
+               wc.wr_id = get_rwqe_ptr(&qp->r_rq, qp->r_rq.tail)->wr_id;
+               if (++qp->r_rq.tail >= qp->r_rq.size)
+                       qp->r_rq.tail = 0;
+               ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc, 1);
+       }
+}
+
+/**
+ * ipath_get_credit - flush the send work queue of a QP
+ * @qp: the qp who's send work queue to flush
+ * @aeth: the Acknowledge Extended Transport Header
+ *
+ * The QP s_lock should be held.
+ */
+void ipath_get_credit(struct ipath_qp *qp, u32 aeth)
+{
+       u32 credit = (aeth >> IPS_AETH_CREDIT_SHIFT) & IPS_AETH_CREDIT_MASK;
+
+       /*
+        * If the credit is invalid, we can send
+        * as many packets as we like.  Otherwise, we have to
+        * honor the credit field.
+        */
+       if (credit == IPS_AETH_CREDIT_INVAL) {
+               qp->s_lsn = (u32) -1;
+       } else if (qp->s_lsn != (u32) -1) {
+               /* Compute new LSN (i.e., MSN + credit) */
+               credit = (aeth + credit_table[credit]) & IPS_MSN_MASK;
+               if (ipath_cmp24(credit, qp->s_lsn) > 0)
+                       qp->s_lsn = credit;
+       }
+
+       /* Restart sending if it was blocked due to lack of credits. */
+       if (qp->s_cur != qp->s_head &&
+           (qp->s_lsn == (u32) -1 ||
+            ipath_cmp24(get_swqe_ptr(qp, qp->s_cur)->ssn,
+                        qp->s_lsn + 1) <= 0))
+               tasklet_hi_schedule(&qp->s_task);
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c
new file mode 100644 (file)
index 0000000..a4055ca
--- /dev/null
@@ -0,0 +1,1857 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ipath_verbs.h"
+#include "ips_common.h"
+
+/* cut down ridiculously long IB macro names */
+#define OP(x) IB_OPCODE_RC_##x
+
+/**
+ * ipath_init_restart- initialize the qp->s_sge after a restart
+ * @qp: the QP who's SGE we're restarting
+ * @wqe: the work queue to initialize the QP's SGE from
+ *
+ * The QP s_lock should be held.
+ */
+static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe)
+{
+       struct ipath_ibdev *dev;
+       u32 len;
+
+       len = ((qp->s_psn - wqe->psn) & IPS_PSN_MASK) *
+               ib_mtu_enum_to_int(qp->path_mtu);
+       qp->s_sge.sge = wqe->sg_list[0];
+       qp->s_sge.sg_list = wqe->sg_list + 1;
+       qp->s_sge.num_sge = wqe->wr.num_sge;
+       ipath_skip_sge(&qp->s_sge, len);
+       qp->s_len = wqe->length - len;
+       dev = to_idev(qp->ibqp.device);
+       spin_lock(&dev->pending_lock);
+       if (qp->timerwait.next == LIST_POISON1)
+               list_add_tail(&qp->timerwait,
+                             &dev->pending[dev->pending_index]);
+       spin_unlock(&dev->pending_lock);
+}
+
+/**
+ * ipath_make_rc_ack - construct a response packet (ACK, NAK, or RDMA read)
+ * @qp: a pointer to the QP
+ * @ohdr: a pointer to the IB header being constructed
+ * @pmtu: the path MTU
+ *
+ * Return bth0 if constructed; otherwise, return 0.
+ * Note the QP s_lock must be held.
+ */
+static inline u32 ipath_make_rc_ack(struct ipath_qp *qp,
+                                   struct ipath_other_headers *ohdr,
+                                   u32 pmtu)
+{
+       struct ipath_sge_state *ss;
+       u32 hwords;
+       u32 len;
+       u32 bth0;
+
+       /* header size in 32-bit words LRH+BTH = (8+12)/4. */
+       hwords = 5;
+
+       /*
+        * Send a response.  Note that we are in the responder's
+        * side of the QP context.
+        */
+       switch (qp->s_ack_state) {
+       case OP(RDMA_READ_REQUEST):
+               ss = &qp->s_rdma_sge;
+               len = qp->s_rdma_len;
+               if (len > pmtu) {
+                       len = pmtu;
+                       qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST);
+               }
+               else
+                       qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY);
+               qp->s_rdma_len -= len;
+               bth0 = qp->s_ack_state << 24;
+               ohdr->u.aeth = ipath_compute_aeth(qp);
+               hwords++;
+               break;
+
+       case OP(RDMA_READ_RESPONSE_FIRST):
+               qp->s_ack_state = OP(RDMA_READ_RESPONSE_MIDDLE);
+               /* FALLTHROUGH */
+       case OP(RDMA_READ_RESPONSE_MIDDLE):
+               ss = &qp->s_rdma_sge;
+               len = qp->s_rdma_len;
+               if (len > pmtu)
+                       len = pmtu;
+               else {
+                       ohdr->u.aeth = ipath_compute_aeth(qp);
+                       hwords++;
+                       qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
+               }
+               qp->s_rdma_len -= len;
+               bth0 = qp->s_ack_state << 24;
+               break;
+
+       case OP(RDMA_READ_RESPONSE_LAST):
+       case OP(RDMA_READ_RESPONSE_ONLY):
+               /*
+                * We have to prevent new requests from changing
+                * the r_sge state while a ipath_verbs_send()
+                * is in progress.
+                * Changing r_state allows the receiver
+                * to continue processing new packets.
+                * We do it here now instead of above so
+                * that we are sure the packet was sent before
+                * changing the state.
+                */
+               qp->r_state = OP(RDMA_READ_RESPONSE_LAST);
+               qp->s_ack_state = OP(ACKNOWLEDGE);
+               return 0;
+
+       case OP(COMPARE_SWAP):
+       case OP(FETCH_ADD):
+               ss = NULL;
+               len = 0;
+               qp->r_state = OP(SEND_LAST);
+               qp->s_ack_state = OP(ACKNOWLEDGE);
+               bth0 = IB_OPCODE_ATOMIC_ACKNOWLEDGE << 24;
+               ohdr->u.at.aeth = ipath_compute_aeth(qp);
+               ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->s_ack_atomic);
+               hwords += sizeof(ohdr->u.at) / 4;
+               break;
+
+       default:
+               /* Send a regular ACK. */
+               ss = NULL;
+               len = 0;
+               qp->s_ack_state = OP(ACKNOWLEDGE);
+               bth0 = qp->s_ack_state << 24;
+               ohdr->u.aeth = ipath_compute_aeth(qp);
+               hwords++;
+       }
+       qp->s_hdrwords = hwords;
+       qp->s_cur_sge = ss;
+       qp->s_cur_size = len;
+
+       return bth0;
+}
+
+/**
+ * ipath_make_rc_req - construct a request packet (SEND, RDMA r/w, ATOMIC)
+ * @qp: a pointer to the QP
+ * @ohdr: a pointer to the IB header being constructed
+ * @pmtu: the path MTU
+ * @bth0p: pointer to the BTH opcode word
+ * @bth2p: pointer to the BTH PSN word
+ *
+ * Return 1 if constructed; otherwise, return 0.
+ * Note the QP s_lock must be held.
+ */
+static inline int ipath_make_rc_req(struct ipath_qp *qp,
+                                   struct ipath_other_headers *ohdr,
+                                   u32 pmtu, u32 *bth0p, u32 *bth2p)
+{
+       struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
+       struct ipath_sge_state *ss;
+       struct ipath_swqe *wqe;
+       u32 hwords;
+       u32 len;
+       u32 bth0;
+       u32 bth2;
+       char newreq;
+
+       if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK) ||
+           qp->s_rnr_timeout)
+               goto done;
+
+       /* header size in 32-bit words LRH+BTH = (8+12)/4. */
+       hwords = 5;
+       bth0 = 0;
+
+       /* Send a request. */
+       wqe = get_swqe_ptr(qp, qp->s_cur);
+       switch (qp->s_state) {
+       default:
+               /*
+                * Resend an old request or start a new one.
+                *
+                * We keep track of the current SWQE so that
+                * we don't reset the "furthest progress" state
+                * if we need to back up.
+                */
+               newreq = 0;
+               if (qp->s_cur == qp->s_tail) {
+                       /* Check if send work queue is empty. */
+                       if (qp->s_tail == qp->s_head)
+                               goto done;
+                       qp->s_psn = wqe->psn = qp->s_next_psn;
+                       newreq = 1;
+               }
+               /*
+                * Note that we have to be careful not to modify the
+                * original work request since we may need to resend
+                * it.
+                */
+               qp->s_sge.sge = wqe->sg_list[0];
+               qp->s_sge.sg_list = wqe->sg_list + 1;
+               qp->s_sge.num_sge = wqe->wr.num_sge;
+               qp->s_len = len = wqe->length;
+               ss = &qp->s_sge;
+               bth2 = 0;
+               switch (wqe->wr.opcode) {
+               case IB_WR_SEND:
+               case IB_WR_SEND_WITH_IMM:
+                       /* If no credit, return. */
+                       if (qp->s_lsn != (u32) -1 &&
+                           ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0)
+                               goto done;
+                       wqe->lpsn = wqe->psn;
+                       if (len > pmtu) {
+                               wqe->lpsn += (len - 1) / pmtu;
+                               qp->s_state = OP(SEND_FIRST);
+                               len = pmtu;
+                               break;
+                       }
+                       if (wqe->wr.opcode == IB_WR_SEND)
+                               qp->s_state = OP(SEND_ONLY);
+                       else {
+                               qp->s_state = OP(SEND_ONLY_WITH_IMMEDIATE);
+                               /* Immediate data comes after the BTH */
+                               ohdr->u.imm_data = wqe->wr.imm_data;
+                               hwords += 1;
+                       }
+                       if (wqe->wr.send_flags & IB_SEND_SOLICITED)
+                               bth0 |= 1 << 23;
+                       bth2 = 1 << 31; /* Request ACK. */
+                       if (++qp->s_cur == qp->s_size)
+                               qp->s_cur = 0;
+                       break;
+
+               case IB_WR_RDMA_WRITE:
+                       if (newreq)
+                               qp->s_lsn++;
+                       /* FALLTHROUGH */
+               case IB_WR_RDMA_WRITE_WITH_IMM:
+                       /* If no credit, return. */
+                       if (qp->s_lsn != (u32) -1 &&
+                           ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0)
+                               goto done;
+                       ohdr->u.rc.reth.vaddr =
+                               cpu_to_be64(wqe->wr.wr.rdma.remote_addr);
+                       ohdr->u.rc.reth.rkey =
+                               cpu_to_be32(wqe->wr.wr.rdma.rkey);
+                       ohdr->u.rc.reth.length = cpu_to_be32(len);
+                       hwords += sizeof(struct ib_reth) / 4;
+                       wqe->lpsn = wqe->psn;
+                       if (len > pmtu) {
+                               wqe->lpsn += (len - 1) / pmtu;
+                               qp->s_state = OP(RDMA_WRITE_FIRST);
+                               len = pmtu;
+                               break;
+                       }
+                       if (wqe->wr.opcode == IB_WR_RDMA_WRITE)
+                               qp->s_state = OP(RDMA_WRITE_ONLY);
+                       else {
+                               qp->s_state =
+                                       OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE);
+                               /* Immediate data comes
+                                * after RETH */
+                               ohdr->u.rc.imm_data = wqe->wr.imm_data;
+                               hwords += 1;
+                               if (wqe->wr.send_flags & IB_SEND_SOLICITED)
+                                       bth0 |= 1 << 23;
+                       }
+                       bth2 = 1 << 31; /* Request ACK. */
+                       if (++qp->s_cur == qp->s_size)
+                               qp->s_cur = 0;
+                       break;
+
+               case IB_WR_RDMA_READ:
+                       ohdr->u.rc.reth.vaddr =
+                               cpu_to_be64(wqe->wr.wr.rdma.remote_addr);
+                       ohdr->u.rc.reth.rkey =
+                               cpu_to_be32(wqe->wr.wr.rdma.rkey);
+                       ohdr->u.rc.reth.length = cpu_to_be32(len);
+                       qp->s_state = OP(RDMA_READ_REQUEST);
+                       hwords += sizeof(ohdr->u.rc.reth) / 4;
+                       if (newreq) {
+                               qp->s_lsn++;
+                               /*
+                                * Adjust s_next_psn to count the
+                                * expected number of responses.
+                                */
+                               if (len > pmtu)
+                                       qp->s_next_psn += (len - 1) / pmtu;
+                               wqe->lpsn = qp->s_next_psn++;
+                       }
+                       ss = NULL;
+                       len = 0;
+                       if (++qp->s_cur == qp->s_size)
+                               qp->s_cur = 0;
+                       break;
+
+               case IB_WR_ATOMIC_CMP_AND_SWP:
+               case IB_WR_ATOMIC_FETCH_AND_ADD:
+                       if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP)
+                               qp->s_state = OP(COMPARE_SWAP);
+                       else
+                               qp->s_state = OP(FETCH_ADD);
+                       ohdr->u.atomic_eth.vaddr = cpu_to_be64(
+                               wqe->wr.wr.atomic.remote_addr);
+                       ohdr->u.atomic_eth.rkey = cpu_to_be32(
+                               wqe->wr.wr.atomic.rkey);
+                       ohdr->u.atomic_eth.swap_data = cpu_to_be64(
+                               wqe->wr.wr.atomic.swap);
+                       ohdr->u.atomic_eth.compare_data = cpu_to_be64(
+                               wqe->wr.wr.atomic.compare_add);
+                       hwords += sizeof(struct ib_atomic_eth) / 4;
+                       if (newreq) {
+                               qp->s_lsn++;
+                               wqe->lpsn = wqe->psn;
+                       }
+                       if (++qp->s_cur == qp->s_size)
+                               qp->s_cur = 0;
+                       ss = NULL;
+                       len = 0;
+                       break;
+
+               default:
+                       goto done;
+               }
+               if (newreq) {
+                       qp->s_tail++;
+                       if (qp->s_tail >= qp->s_size)
+                               qp->s_tail = 0;
+               }
+               bth2 |= qp->s_psn++ & IPS_PSN_MASK;
+               if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+                       qp->s_next_psn = qp->s_psn;
+               spin_lock(&dev->pending_lock);
+               if (qp->timerwait.next == LIST_POISON1)
+                       list_add_tail(&qp->timerwait,
+                                     &dev->pending[dev->pending_index]);
+               spin_unlock(&dev->pending_lock);
+               break;
+
+       case OP(RDMA_READ_RESPONSE_FIRST):
+               /*
+                * This case can only happen if a send is restarted.  See
+                * ipath_restart_rc().
+                */
+               ipath_init_restart(qp, wqe);
+               /* FALLTHROUGH */
+       case OP(SEND_FIRST):
+               qp->s_state = OP(SEND_MIDDLE);
+               /* FALLTHROUGH */
+       case OP(SEND_MIDDLE):
+               bth2 = qp->s_psn++ & IPS_PSN_MASK;
+               if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+                       qp->s_next_psn = qp->s_psn;
+               ss = &qp->s_sge;
+               len = qp->s_len;
+               if (len > pmtu) {
+                       /*
+                        * Request an ACK every 1/2 MB to avoid retransmit
+                        * timeouts.
+                        */
+                       if (((wqe->length - len) % (512 * 1024)) == 0)
+                               bth2 |= 1 << 31;
+                       len = pmtu;
+                       break;
+               }
+               if (wqe->wr.opcode == IB_WR_SEND)
+                       qp->s_state = OP(SEND_LAST);
+               else {
+                       qp->s_state = OP(SEND_LAST_WITH_IMMEDIATE);
+                       /* Immediate data comes after the BTH */
+                       ohdr->u.imm_data = wqe->wr.imm_data;
+                       hwords += 1;
+               }
+               if (wqe->wr.send_flags & IB_SEND_SOLICITED)
+                       bth0 |= 1 << 23;
+               bth2 |= 1 << 31;        /* Request ACK. */
+               qp->s_cur++;
+               if (qp->s_cur >= qp->s_size)
+                       qp->s_cur = 0;
+               break;
+
+       case OP(RDMA_READ_RESPONSE_LAST):
+               /*
+                * This case can only happen if a RDMA write is restarted.
+                * See ipath_restart_rc().
+                */
+               ipath_init_restart(qp, wqe);
+               /* FALLTHROUGH */
+       case OP(RDMA_WRITE_FIRST):
+               qp->s_state = OP(RDMA_WRITE_MIDDLE);
+               /* FALLTHROUGH */
+       case OP(RDMA_WRITE_MIDDLE):
+               bth2 = qp->s_psn++ & IPS_PSN_MASK;
+               if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+                       qp->s_next_psn = qp->s_psn;
+               ss = &qp->s_sge;
+               len = qp->s_len;
+               if (len > pmtu) {
+                       /*
+                        * Request an ACK every 1/2 MB to avoid retransmit
+                        * timeouts.
+                        */
+                       if (((wqe->length - len) % (512 * 1024)) == 0)
+                               bth2 |= 1 << 31;
+                       len = pmtu;
+                       break;
+               }
+               if (wqe->wr.opcode == IB_WR_RDMA_WRITE)
+                       qp->s_state = OP(RDMA_WRITE_LAST);
+               else {
+                       qp->s_state = OP(RDMA_WRITE_LAST_WITH_IMMEDIATE);
+                       /* Immediate data comes after the BTH */
+                       ohdr->u.imm_data = wqe->wr.imm_data;
+                       hwords += 1;
+                       if (wqe->wr.send_flags & IB_SEND_SOLICITED)
+                               bth0 |= 1 << 23;
+               }
+               bth2 |= 1 << 31;        /* Request ACK. */
+               qp->s_cur++;
+               if (qp->s_cur >= qp->s_size)
+                       qp->s_cur = 0;
+               break;
+
+       case OP(RDMA_READ_RESPONSE_MIDDLE):
+               /*
+                * This case can only happen if a RDMA read is restarted.
+                * See ipath_restart_rc().
+                */
+               ipath_init_restart(qp, wqe);
+               len = ((qp->s_psn - wqe->psn) & IPS_PSN_MASK) * pmtu;
+               ohdr->u.rc.reth.vaddr =
+                       cpu_to_be64(wqe->wr.wr.rdma.remote_addr + len);
+               ohdr->u.rc.reth.rkey =
+                       cpu_to_be32(wqe->wr.wr.rdma.rkey);
+               ohdr->u.rc.reth.length = cpu_to_be32(qp->s_len);
+               qp->s_state = OP(RDMA_READ_REQUEST);
+               hwords += sizeof(ohdr->u.rc.reth) / 4;
+               bth2 = qp->s_psn++ & IPS_PSN_MASK;
+               if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+                       qp->s_next_psn = qp->s_psn;
+               ss = NULL;
+               len = 0;
+               qp->s_cur++;
+               if (qp->s_cur == qp->s_size)
+                       qp->s_cur = 0;
+               break;
+
+       case OP(RDMA_READ_REQUEST):
+       case OP(COMPARE_SWAP):
+       case OP(FETCH_ADD):
+               /*
+                * We shouldn't start anything new until this request is
+                * finished.  The ACK will handle rescheduling us.  XXX The
+                * number of outstanding ones is negotiated at connection
+                * setup time (see pg. 258,289)?  XXX Also, if we support
+                * multiple outstanding requests, we need to check the WQE
+                * IB_SEND_FENCE flag and not send a new request if a RDMA
+                * read or atomic is pending.
+                */
+               goto done;
+       }
+       qp->s_len -= len;
+       qp->s_hdrwords = hwords;
+       qp->s_cur_sge = ss;
+       qp->s_cur_size = len;
+       *bth0p = bth0 | (qp->s_state << 24);
+       *bth2p = bth2;
+       return 1;
+
+done:
+       return 0;
+}
+
+static inline void ipath_make_rc_grh(struct ipath_qp *qp,
+                                    struct ib_global_route *grh,
+                                    u32 nwords)
+{
+       struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
+
+       /* GRH header size in 32-bit words. */
+       qp->s_hdrwords += 10;
+       qp->s_hdr.u.l.grh.version_tclass_flow =
+               cpu_to_be32((6 << 28) |
+                           (grh->traffic_class << 20) |
+                           grh->flow_label);
+       qp->s_hdr.u.l.grh.paylen =
+               cpu_to_be16(((qp->s_hdrwords - 12) + nwords +
+                            SIZE_OF_CRC) << 2);
+       /* next_hdr is defined by C8-7 in ch. 8.4.1 */
+       qp->s_hdr.u.l.grh.next_hdr = 0x1B;
+       qp->s_hdr.u.l.grh.hop_limit = grh->hop_limit;
+       /* The SGID is 32-bit aligned. */
+       qp->s_hdr.u.l.grh.sgid.global.subnet_prefix = dev->gid_prefix;
+       qp->s_hdr.u.l.grh.sgid.global.interface_id =
+               ipath_layer_get_guid(dev->dd);
+       qp->s_hdr.u.l.grh.dgid = grh->dgid;
+}
+
+/**
+ * ipath_do_rc_send - perform a send on an RC QP
+ * @data: contains a pointer to the QP
+ *
+ * Process entries in the send work queue until credit or queue is
+ * exhausted.  Only allow one CPU to send a packet per QP (tasklet).
+ * Otherwise, after we drop the QP s_lock, two threads could send
+ * packets out of order.
+ */
+void ipath_do_rc_send(unsigned long data)
+{
+       struct ipath_qp *qp = (struct ipath_qp *)data;
+       struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
+       unsigned long flags;
+       u16 lrh0;
+       u32 nwords;
+       u32 extra_bytes;
+       u32 bth0;
+       u32 bth2;
+       u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+       struct ipath_other_headers *ohdr;
+
+       if (test_and_set_bit(IPATH_S_BUSY, &qp->s_flags))
+               goto bail;
+
+       if (unlikely(qp->remote_ah_attr.dlid ==
+                    ipath_layer_get_lid(dev->dd))) {
+               struct ib_wc wc;
+
+               /*
+                * Pass in an uninitialized ib_wc to be consistent with
+                * other places where ipath_ruc_loopback() is called.
+                */
+               ipath_ruc_loopback(qp, &wc);
+               goto clear;
+       }
+
+       ohdr = &qp->s_hdr.u.oth;
+       if (qp->remote_ah_attr.ah_flags & IB_AH_GRH)
+               ohdr = &qp->s_hdr.u.l.oth;
+
+again:
+       /* Check for a constructed packet to be sent. */
+       if (qp->s_hdrwords != 0) {
+               /*
+                * If no PIO bufs are available, return.  An interrupt will
+                * call ipath_ib_piobufavail() when one is available.
+                */
+               _VERBS_INFO("h %u %p\n", qp->s_hdrwords, &qp->s_hdr);
+               _VERBS_INFO("d %u %p %u %p %u %u %u %u\n", qp->s_cur_size,
+                           qp->s_cur_sge->sg_list,
+                           qp->s_cur_sge->num_sge,
+                           qp->s_cur_sge->sge.vaddr,
+                           qp->s_cur_sge->sge.sge_length,
+                           qp->s_cur_sge->sge.length,
+                           qp->s_cur_sge->sge.m,
+                           qp->s_cur_sge->sge.n);
+               if (ipath_verbs_send(dev->dd, qp->s_hdrwords,
+                                    (u32 *) &qp->s_hdr, qp->s_cur_size,
+                                    qp->s_cur_sge)) {
+                       ipath_no_bufs_available(qp, dev);
+                       goto bail;
+               }
+               dev->n_unicast_xmit++;
+               /* Record that we sent the packet and s_hdr is empty. */
+               qp->s_hdrwords = 0;
+       }
+
+       /*
+        * The lock is needed to synchronize between setting
+        * qp->s_ack_state, resend timer, and post_send().
+        */
+       spin_lock_irqsave(&qp->s_lock, flags);
+
+       /* Sending responses has higher priority over sending requests. */
+       if (qp->s_ack_state != OP(ACKNOWLEDGE) &&
+           (bth0 = ipath_make_rc_ack(qp, ohdr, pmtu)) != 0)
+               bth2 = qp->s_ack_psn++ & IPS_PSN_MASK;
+       else if (!ipath_make_rc_req(qp, ohdr, pmtu, &bth0, &bth2))
+               goto done;
+
+       spin_unlock_irqrestore(&qp->s_lock, flags);
+
+       /* Construct the header. */
+       extra_bytes = (4 - qp->s_cur_size) & 3;
+       nwords = (qp->s_cur_size + extra_bytes) >> 2;
+       lrh0 = IPS_LRH_BTH;
+       if (unlikely(qp->remote_ah_attr.ah_flags & IB_AH_GRH)) {
+               ipath_make_rc_grh(qp, &qp->remote_ah_attr.grh, nwords);
+               lrh0 = IPS_LRH_GRH;
+       }
+       lrh0 |= qp->remote_ah_attr.sl << 4;
+       qp->s_hdr.lrh[0] = cpu_to_be16(lrh0);
+       qp->s_hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid);
+       qp->s_hdr.lrh[2] = cpu_to_be16(qp->s_hdrwords + nwords +
+                                      SIZE_OF_CRC);
+       qp->s_hdr.lrh[3] = cpu_to_be16(ipath_layer_get_lid(dev->dd));
+       bth0 |= ipath_layer_get_pkey(dev->dd, qp->s_pkey_index);
+       bth0 |= extra_bytes << 20;
+       ohdr->bth[0] = cpu_to_be32(bth0);
+       ohdr->bth[1] = cpu_to_be32(qp->remote_qpn);
+       ohdr->bth[2] = cpu_to_be32(bth2);
+
+       /* Check for more work to do. */
+       goto again;
+
+done:
+       spin_unlock_irqrestore(&qp->s_lock, flags);
+clear:
+       clear_bit(IPATH_S_BUSY, &qp->s_flags);
+bail:
+       return;
+}
+
+static void send_rc_ack(struct ipath_qp *qp)
+{
+       struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
+       u16 lrh0;
+       u32 bth0;
+       struct ipath_other_headers *ohdr;
+
+       /* Construct the header. */
+       ohdr = &qp->s_hdr.u.oth;
+       lrh0 = IPS_LRH_BTH;
+       /* header size in 32-bit words LRH+BTH+AETH = (8+12+4)/4. */
+       qp->s_hdrwords = 6;
+       if (unlikely(qp->remote_ah_attr.ah_flags & IB_AH_GRH)) {
+               ipath_make_rc_grh(qp, &qp->remote_ah_attr.grh, 0);
+               ohdr = &qp->s_hdr.u.l.oth;
+               lrh0 = IPS_LRH_GRH;
+       }
+       bth0 = ipath_layer_get_pkey(dev->dd, qp->s_pkey_index);
+       ohdr->u.aeth = ipath_compute_aeth(qp);
+       if (qp->s_ack_state >= OP(COMPARE_SWAP)) {
+               bth0 |= IB_OPCODE_ATOMIC_ACKNOWLEDGE << 24;
+               ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->s_ack_atomic);
+               qp->s_hdrwords += sizeof(ohdr->u.at.atomic_ack_eth) / 4;
+       }
+       else
+               bth0 |= OP(ACKNOWLEDGE) << 24;
+       lrh0 |= qp->remote_ah_attr.sl << 4;
+       qp->s_hdr.lrh[0] = cpu_to_be16(lrh0);
+       qp->s_hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid);
+       qp->s_hdr.lrh[2] = cpu_to_be16(qp->s_hdrwords + SIZE_OF_CRC);
+       qp->s_hdr.lrh[3] = cpu_to_be16(ipath_layer_get_lid(dev->dd));
+       ohdr->bth[0] = cpu_to_be32(bth0);
+       ohdr->bth[1] = cpu_to_be32(qp->remote_qpn);
+       ohdr->bth[2] = cpu_to_be32(qp->s_ack_psn & IPS_PSN_MASK);
+
+       /*
+        * If we can send the ACK, clear the ACK state.
+        */
+       if (ipath_verbs_send(dev->dd, qp->s_hdrwords, (u32 *) &qp->s_hdr,
+                            0, NULL) == 0) {
+               qp->s_ack_state = OP(ACKNOWLEDGE);
+               dev->n_rc_qacks++;
+               dev->n_unicast_xmit++;
+       }
+}
+
+/**
+ * ipath_restart_rc - back up requester to resend the last un-ACKed request
+ * @qp: the QP to restart
+ * @psn: packet sequence number for the request
+ * @wc: the work completion request
+ *
+ * The QP s_lock should be held.
+ */
+void ipath_restart_rc(struct ipath_qp *qp, u32 psn, struct ib_wc *wc)
+{
+       struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last);
+       struct ipath_ibdev *dev;
+       u32 n;
+
+       /*
+        * If there are no requests pending, we are done.
+        */
+       if (ipath_cmp24(psn, qp->s_next_psn) >= 0 ||
+           qp->s_last == qp->s_tail)
+               goto done;
+
+       if (qp->s_retry == 0) {
+               wc->wr_id = wqe->wr.wr_id;
+               wc->status = IB_WC_RETRY_EXC_ERR;
+               wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
+               wc->vendor_err = 0;
+               wc->byte_len = 0;
+               wc->qp_num = qp->ibqp.qp_num;
+               wc->src_qp = qp->remote_qpn;
+               wc->pkey_index = 0;
+               wc->slid = qp->remote_ah_attr.dlid;
+               wc->sl = qp->remote_ah_attr.sl;
+               wc->dlid_path_bits = 0;
+               wc->port_num = 0;
+               ipath_sqerror_qp(qp, wc);
+               goto bail;
+       }
+       qp->s_retry--;
+
+       /*
+        * Remove the QP from the timeout queue.
+        * Note: it may already have been removed by ipath_ib_timer().
+        */
+       dev = to_idev(qp->ibqp.device);
+       spin_lock(&dev->pending_lock);
+       if (qp->timerwait.next != LIST_POISON1)
+               list_del(&qp->timerwait);
+       spin_unlock(&dev->pending_lock);
+
+       if (wqe->wr.opcode == IB_WR_RDMA_READ)
+               dev->n_rc_resends++;
+       else
+               dev->n_rc_resends += (int)qp->s_psn - (int)psn;
+
+       /*
+        * If we are starting the request from the beginning, let the normal
+        * send code handle initialization.
+        */
+       qp->s_cur = qp->s_last;
+       if (ipath_cmp24(psn, wqe->psn) <= 0) {
+               qp->s_state = OP(SEND_LAST);
+               qp->s_psn = wqe->psn;
+       } else {
+               n = qp->s_cur;
+               for (;;) {
+                       if (++n == qp->s_size)
+                               n = 0;
+                       if (n == qp->s_tail) {
+                               if (ipath_cmp24(psn, qp->s_next_psn) >= 0) {
+                                       qp->s_cur = n;
+                                       wqe = get_swqe_ptr(qp, n);
+                               }
+                               break;
+                       }
+                       wqe = get_swqe_ptr(qp, n);
+                       if (ipath_cmp24(psn, wqe->psn) < 0)
+                               break;
+                       qp->s_cur = n;
+               }
+               qp->s_psn = psn;
+
+               /*
+                * Reset the state to restart in the middle of a request.
+                * Don't change the s_sge, s_cur_sge, or s_cur_size.
+                * See ipath_do_rc_send().
+                */
+               switch (wqe->wr.opcode) {
+               case IB_WR_SEND:
+               case IB_WR_SEND_WITH_IMM:
+                       qp->s_state = OP(RDMA_READ_RESPONSE_FIRST);
+                       break;
+
+               case IB_WR_RDMA_WRITE:
+               case IB_WR_RDMA_WRITE_WITH_IMM:
+                       qp->s_state = OP(RDMA_READ_RESPONSE_LAST);
+                       break;
+
+               case IB_WR_RDMA_READ:
+                       qp->s_state =
+                               OP(RDMA_READ_RESPONSE_MIDDLE);
+                       break;
+
+               default:
+                       /*
+                        * This case shouldn't happen since its only
+                        * one PSN per req.
+                        */
+                       qp->s_state = OP(SEND_LAST);
+               }
+       }
+
+done:
+       tasklet_hi_schedule(&qp->s_task);
+
+bail:
+       return;
+}
+
+/**
+ * reset_psn - reset the QP state to send starting from PSN
+ * @qp: the QP
+ * @psn: the packet sequence number to restart at
+ *
+ * This is called from ipath_rc_rcv() to process an incoming RC ACK
+ * for the given QP.
+ * Called at interrupt level with the QP s_lock held.
+ */
+static void reset_psn(struct ipath_qp *qp, u32 psn)
+{
+       struct ipath_swqe *wqe;
+       u32 n;
+
+       n = qp->s_cur;
+       wqe = get_swqe_ptr(qp, n);
+       for (;;) {
+               if (++n == qp->s_size)
+                       n = 0;
+               if (n == qp->s_tail) {
+                       if (ipath_cmp24(psn, qp->s_next_psn) >= 0) {
+                               qp->s_cur = n;
+                               wqe = get_swqe_ptr(qp, n);
+                       }
+                       break;
+               }
+               wqe = get_swqe_ptr(qp, n);
+               if (ipath_cmp24(psn, wqe->psn) < 0)
+                       break;
+               qp->s_cur = n;
+       }
+       qp->s_psn = psn;
+
+       /*
+        * Set the state to restart in the middle of a
+        * request.  Don't change the s_sge, s_cur_sge, or
+        * s_cur_size.  See ipath_do_rc_send().
+        */
+       switch (wqe->wr.opcode) {
+       case IB_WR_SEND:
+       case IB_WR_SEND_WITH_IMM:
+               qp->s_state = OP(RDMA_READ_RESPONSE_FIRST);
+               break;
+
+       case IB_WR_RDMA_WRITE:
+       case IB_WR_RDMA_WRITE_WITH_IMM:
+               qp->s_state = OP(RDMA_READ_RESPONSE_LAST);
+               break;
+
+       case IB_WR_RDMA_READ:
+               qp->s_state = OP(RDMA_READ_RESPONSE_MIDDLE);
+               break;
+
+       default:
+               /*
+                * This case shouldn't happen since its only
+                * one PSN per req.
+                */
+               qp->s_state = OP(SEND_LAST);
+       }
+}
+
+/**
+ * do_rc_ack - process an incoming RC ACK
+ * @qp: the QP the ACK came in on
+ * @psn: the packet sequence number of the ACK
+ * @opcode: the opcode of the request that resulted in the ACK
+ *
+ * This is called from ipath_rc_rcv() to process an incoming RC ACK
+ * for the given QP.
+ * Called at interrupt level with the QP s_lock held.
+ * Returns 1 if OK, 0 if current operation should be aborted (NAK).
+ */
+static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
+{
+       struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
+       struct ib_wc wc;
+       struct ipath_swqe *wqe;
+       int ret = 0;
+
+       /*
+        * Remove the QP from the timeout queue (or RNR timeout queue).
+        * If ipath_ib_timer() has already removed it,
+        * it's OK since we hold the QP s_lock and ipath_restart_rc()
+        * just won't find anything to restart if we ACK everything.
+        */
+       spin_lock(&dev->pending_lock);
+       if (qp->timerwait.next != LIST_POISON1)
+               list_del(&qp->timerwait);
+       spin_unlock(&dev->pending_lock);
+
+       /*
+        * Note that NAKs implicitly ACK outstanding SEND and RDMA write
+        * requests and implicitly NAK RDMA read and atomic requests issued
+        * before the NAK'ed request.  The MSN won't include the NAK'ed
+        * request but will include an ACK'ed request(s).
+        */
+       wqe = get_swqe_ptr(qp, qp->s_last);
+
+       /* Nothing is pending to ACK/NAK. */
+       if (qp->s_last == qp->s_tail)
+               goto bail;
+
+       /*
+        * The MSN might be for a later WQE than the PSN indicates so
+        * only complete WQEs that the PSN finishes.
+        */
+       while (ipath_cmp24(psn, wqe->lpsn) >= 0) {
+               /* If we are ACKing a WQE, the MSN should be >= the SSN. */
+               if (ipath_cmp24(aeth, wqe->ssn) < 0)
+                       break;
+               /*
+                * If this request is a RDMA read or atomic, and the ACK is
+                * for a later operation, this ACK NAKs the RDMA read or
+                * atomic.  In other words, only a RDMA_READ_LAST or ONLY
+                * can ACK a RDMA read and likewise for atomic ops.  Note
+                * that the NAK case can only happen if relaxed ordering is
+                * used and requests are sent after an RDMA read or atomic
+                * is sent but before the response is received.
+                */
+               if ((wqe->wr.opcode == IB_WR_RDMA_READ &&
+                    opcode != OP(RDMA_READ_RESPONSE_LAST)) ||
+                   ((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
+                     wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) &&
+                    (opcode != OP(ATOMIC_ACKNOWLEDGE) ||
+                     ipath_cmp24(wqe->psn, psn) != 0))) {
+                       /*
+                        * The last valid PSN seen is the previous
+                        * request's.
+                        */
+                       qp->s_last_psn = wqe->psn - 1;
+                       /* Retry this request. */
+                       ipath_restart_rc(qp, wqe->psn, &wc);
+                       /*
+                        * No need to process the ACK/NAK since we are
+                        * restarting an earlier request.
+                        */
+                       goto bail;
+               }
+               /* Post a send completion queue entry if requested. */
+               if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) ||
+                   (wqe->wr.send_flags & IB_SEND_SIGNALED)) {
+                       wc.wr_id = wqe->wr.wr_id;
+                       wc.status = IB_WC_SUCCESS;
+                       wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
+                       wc.vendor_err = 0;
+                       wc.byte_len = wqe->length;
+                       wc.qp_num = qp->ibqp.qp_num;
+                       wc.src_qp = qp->remote_qpn;
+                       wc.pkey_index = 0;
+                       wc.slid = qp->remote_ah_attr.dlid;
+                       wc.sl = qp->remote_ah_attr.sl;
+                       wc.dlid_path_bits = 0;
+                       wc.port_num = 0;
+                       ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 0);
+               }
+               qp->s_retry = qp->s_retry_cnt;
+               /*
+                * If we are completing a request which is in the process of
+                * being resent, we can stop resending it since we know the
+                * responder has already seen it.
+                */
+               if (qp->s_last == qp->s_cur) {
+                       if (++qp->s_cur >= qp->s_size)
+                               qp->s_cur = 0;
+                       wqe = get_swqe_ptr(qp, qp->s_cur);
+                       qp->s_state = OP(SEND_LAST);
+                       qp->s_psn = wqe->psn;
+               }
+               if (++qp->s_last >= qp->s_size)
+                       qp->s_last = 0;
+               wqe = get_swqe_ptr(qp, qp->s_last);
+               if (qp->s_last == qp->s_tail)
+                       break;
+       }
+
+       switch (aeth >> 29) {
+       case 0:         /* ACK */
+               dev->n_rc_acks++;
+               /* If this is a partial ACK, reset the retransmit timer. */
+               if (qp->s_last != qp->s_tail) {
+                       spin_lock(&dev->pending_lock);
+                       list_add_tail(&qp->timerwait,
+                                     &dev->pending[dev->pending_index]);
+                       spin_unlock(&dev->pending_lock);
+               }
+               ipath_get_credit(qp, aeth);
+               qp->s_rnr_retry = qp->s_rnr_retry_cnt;
+               qp->s_retry = qp->s_retry_cnt;
+               qp->s_last_psn = psn;
+               ret = 1;
+               goto bail;
+
+       case 1:         /* RNR NAK */
+               dev->n_rnr_naks++;
+               if (qp->s_rnr_retry == 0) {
+                       if (qp->s_last == qp->s_tail)
+                               goto bail;
+
+                       wc.status = IB_WC_RNR_RETRY_EXC_ERR;
+                       goto class_b;
+               }
+               if (qp->s_rnr_retry_cnt < 7)
+                       qp->s_rnr_retry--;
+               if (qp->s_last == qp->s_tail)
+                       goto bail;
+
+               /* The last valid PSN seen is the previous request's. */
+               qp->s_last_psn = wqe->psn - 1;
+
+               dev->n_rc_resends += (int)qp->s_psn - (int)psn;
+
+               /*
+                * If we are starting the request from the beginning, let
+                * the normal send code handle initialization.
+                */
+               qp->s_cur = qp->s_last;
+               wqe = get_swqe_ptr(qp, qp->s_cur);
+               if (ipath_cmp24(psn, wqe->psn) <= 0) {
+                       qp->s_state = OP(SEND_LAST);
+                       qp->s_psn = wqe->psn;
+               } else
+                       reset_psn(qp, psn);
+
+               qp->s_rnr_timeout =
+                       ib_ipath_rnr_table[(aeth >> IPS_AETH_CREDIT_SHIFT) &
+                                          IPS_AETH_CREDIT_MASK];
+               ipath_insert_rnr_queue(qp);
+               goto bail;
+
+       case 3:         /* NAK */
+               /* The last valid PSN seen is the previous request's. */
+               if (qp->s_last != qp->s_tail)
+                       qp->s_last_psn = wqe->psn - 1;
+               switch ((aeth >> IPS_AETH_CREDIT_SHIFT) &
+                       IPS_AETH_CREDIT_MASK) {
+               case 0: /* PSN sequence error */
+                       dev->n_seq_naks++;
+                       /*
+                        * Back up to the responder's expected PSN.  XXX
+                        * Note that we might get a NAK in the middle of an
+                        * RDMA READ response which terminates the RDMA
+                        * READ.
+                        */
+                       if (qp->s_last == qp->s_tail)
+                               break;
+
+                       if (ipath_cmp24(psn, wqe->psn) < 0)
+                               break;
+
+                       /* Retry the request. */
+                       ipath_restart_rc(qp, psn, &wc);
+                       break;
+
+               case 1: /* Invalid Request */
+                       wc.status = IB_WC_REM_INV_REQ_ERR;
+                       dev->n_other_naks++;
+                       goto class_b;
+
+               case 2: /* Remote Access Error */
+                       wc.status = IB_WC_REM_ACCESS_ERR;
+                       dev->n_other_naks++;
+                       goto class_b;
+
+               case 3: /* Remote Operation Error */
+                       wc.status = IB_WC_REM_OP_ERR;
+                       dev->n_other_naks++;
+               class_b:
+                       wc.wr_id = wqe->wr.wr_id;
+                       wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
+                       wc.vendor_err = 0;
+                       wc.byte_len = 0;
+                       wc.qp_num = qp->ibqp.qp_num;
+                       wc.src_qp = qp->remote_qpn;
+                       wc.pkey_index = 0;
+                       wc.slid = qp->remote_ah_attr.dlid;
+                       wc.sl = qp->remote_ah_attr.sl;
+                       wc.dlid_path_bits = 0;
+                       wc.port_num = 0;
+                       ipath_sqerror_qp(qp, &wc);
+                       break;
+
+               default:
+                       /* Ignore other reserved NAK error codes */
+                       goto reserved;
+               }
+               qp->s_rnr_retry = qp->s_rnr_retry_cnt;
+               goto bail;
+
+       default:                /* 2: reserved */
+       reserved:
+               /* Ignore reserved NAK codes. */
+               goto bail;
+       }
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_rc_rcv_resp - process an incoming RC response packet
+ * @dev: the device this packet came in on
+ * @ohdr: the other headers for this packet
+ * @data: the packet data
+ * @tlen: the packet length
+ * @qp: the QP for this packet
+ * @opcode: the opcode for this packet
+ * @psn: the packet sequence number for this packet
+ * @hdrsize: the header length
+ * @pmtu: the path MTU
+ * @header_in_data: true if part of the header data is in the data buffer
+ *
+ * This is called from ipath_rc_rcv() to process an incoming RC response
+ * packet for the given QP.
+ * Called at interrupt level.
+ */
+static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
+                                    struct ipath_other_headers *ohdr,
+                                    void *data, u32 tlen,
+                                    struct ipath_qp *qp,
+                                    u32 opcode,
+                                    u32 psn, u32 hdrsize, u32 pmtu,
+                                    int header_in_data)
+{
+       unsigned long flags;
+       struct ib_wc wc;
+       int diff;
+       u32 pad;
+       u32 aeth;
+
+       spin_lock_irqsave(&qp->s_lock, flags);
+
+       /* Ignore invalid responses. */
+       if (ipath_cmp24(psn, qp->s_next_psn) >= 0)
+               goto ack_done;
+
+       /* Ignore duplicate responses. */
+       diff = ipath_cmp24(psn, qp->s_last_psn);
+       if (unlikely(diff <= 0)) {
+               /* Update credits for "ghost" ACKs */
+               if (diff == 0 && opcode == OP(ACKNOWLEDGE)) {
+                       if (!header_in_data)
+                               aeth = be32_to_cpu(ohdr->u.aeth);
+                       else {
+                               aeth = be32_to_cpu(((__be32 *) data)[0]);
+                               data += sizeof(__be32);
+                       }
+                       if ((aeth >> 29) == 0)
+                               ipath_get_credit(qp, aeth);
+               }
+               goto ack_done;
+       }
+
+       switch (opcode) {
+       case OP(ACKNOWLEDGE):
+       case OP(ATOMIC_ACKNOWLEDGE):
+       case OP(RDMA_READ_RESPONSE_FIRST):
+               if (!header_in_data)
+                       aeth = be32_to_cpu(ohdr->u.aeth);
+               else {
+                       aeth = be32_to_cpu(((__be32 *) data)[0]);
+                       data += sizeof(__be32);
+               }
+               if (opcode == OP(ATOMIC_ACKNOWLEDGE))
+                       *(u64 *) qp->s_sge.sge.vaddr = *(u64 *) data;
+               if (!do_rc_ack(qp, aeth, psn, opcode) ||
+                   opcode != OP(RDMA_READ_RESPONSE_FIRST))
+                       goto ack_done;
+               hdrsize += 4;
+               /*
+                * do_rc_ack() has already checked the PSN so skip
+                * the sequence check.
+                */
+               goto rdma_read;
+
+       case OP(RDMA_READ_RESPONSE_MIDDLE):
+               /* no AETH, no ACK */
+               if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
+                       dev->n_rdma_seq++;
+                       ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
+                       goto ack_done;
+               }
+       rdma_read:
+       if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST)))
+               goto ack_done;
+       if (unlikely(tlen != (hdrsize + pmtu + 4)))
+               goto ack_done;
+       if (unlikely(pmtu >= qp->s_len))
+               goto ack_done;
+       /* We got a response so update the timeout. */
+       if (unlikely(qp->s_last == qp->s_tail ||
+                    get_swqe_ptr(qp, qp->s_last)->wr.opcode !=
+                    IB_WR_RDMA_READ))
+               goto ack_done;
+       spin_lock(&dev->pending_lock);
+       if (qp->s_rnr_timeout == 0 &&
+           qp->timerwait.next != LIST_POISON1)
+               list_move_tail(&qp->timerwait,
+                              &dev->pending[dev->pending_index]);
+       spin_unlock(&dev->pending_lock);
+       /*
+        * Update the RDMA receive state but do the copy w/o holding the
+        * locks and blocking interrupts.  XXX Yet another place that
+        * affects relaxed RDMA order since we don't want s_sge modified.
+        */
+       qp->s_len -= pmtu;
+       qp->s_last_psn = psn;
+       spin_unlock_irqrestore(&qp->s_lock, flags);
+       ipath_copy_sge(&qp->s_sge, data, pmtu);
+       goto bail;
+
+       case OP(RDMA_READ_RESPONSE_LAST):
+               /* ACKs READ req. */
+               if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
+                       dev->n_rdma_seq++;
+                       ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
+                       goto ack_done;
+               }
+               /* FALLTHROUGH */
+       case OP(RDMA_READ_RESPONSE_ONLY):
+               if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST)))
+                       goto ack_done;
+               /*
+                * Get the number of bytes the message was padded by.
+                */
+               pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
+               /*
+                * Check that the data size is >= 1 && <= pmtu.
+                * Remember to account for the AETH header (4) and
+                * ICRC (4).
+                */
+               if (unlikely(tlen <= (hdrsize + pad + 8))) {
+                       /*
+                        * XXX Need to generate an error CQ
+                        * entry.
+                        */
+                       goto ack_done;
+               }
+               tlen -= hdrsize + pad + 8;
+               if (unlikely(tlen != qp->s_len)) {
+                       /*
+                        * XXX Need to generate an error CQ
+                        * entry.
+                        */
+                       goto ack_done;
+               }
+               if (!header_in_data)
+                       aeth = be32_to_cpu(ohdr->u.aeth);
+               else {
+                       aeth = be32_to_cpu(((__be32 *) data)[0]);
+                       data += sizeof(__be32);
+               }
+               ipath_copy_sge(&qp->s_sge, data, tlen);
+               if (do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST))) {
+                       /*
+                        * Change the state so we contimue
+                        * processing new requests.
+                        */
+                       qp->s_state = OP(SEND_LAST);
+               }
+               goto ack_done;
+       }
+
+ack_done:
+       spin_unlock_irqrestore(&qp->s_lock, flags);
+bail:
+       return;
+}
+
+/**
+ * ipath_rc_rcv_error - process an incoming duplicate or error RC packet
+ * @dev: the device this packet came in on
+ * @ohdr: the other headers for this packet
+ * @data: the packet data
+ * @qp: the QP for this packet
+ * @opcode: the opcode for this packet
+ * @psn: the packet sequence number for this packet
+ * @diff: the difference between the PSN and the expected PSN
+ * @header_in_data: true if part of the header data is in the data buffer
+ *
+ * This is called from ipath_rc_rcv() to process an unexpected
+ * incoming RC packet for the given QP.
+ * Called at interrupt level.
+ * Return 1 if no more processing is needed; otherwise return 0 to
+ * schedule a response to be sent and the s_lock unlocked.
+ */
+static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
+                                    struct ipath_other_headers *ohdr,
+                                    void *data,
+                                    struct ipath_qp *qp,
+                                    u32 opcode,
+                                    u32 psn,
+                                    int diff,
+                                    int header_in_data)
+{
+       struct ib_reth *reth;
+
+       if (diff > 0) {
+               /*
+                * Packet sequence error.
+                * A NAK will ACK earlier sends and RDMA writes.
+                * Don't queue the NAK if a RDMA read, atomic, or
+                * NAK is pending though.
+                */
+               spin_lock(&qp->s_lock);
+               if ((qp->s_ack_state >= OP(RDMA_READ_REQUEST) &&
+                    qp->s_ack_state != IB_OPCODE_ACKNOWLEDGE) ||
+                   qp->s_nak_state != 0) {
+                       spin_unlock(&qp->s_lock);
+                       goto done;
+               }
+               qp->s_ack_state = OP(SEND_ONLY);
+               qp->s_nak_state = IB_NAK_PSN_ERROR;
+               /* Use the expected PSN. */
+               qp->s_ack_psn = qp->r_psn;
+               goto resched;
+       }
+
+       /*
+        * Handle a duplicate request.  Don't re-execute SEND, RDMA
+        * write or atomic op.  Don't NAK errors, just silently drop
+        * the duplicate request.  Note that r_sge, r_len, and
+        * r_rcv_len may be in use so don't modify them.
+        *
+        * We are supposed to ACK the earliest duplicate PSN but we
+        * can coalesce an outstanding duplicate ACK.  We have to
+        * send the earliest so that RDMA reads can be restarted at
+        * the requester's expected PSN.
+        */
+       spin_lock(&qp->s_lock);
+       if (qp->s_ack_state != IB_OPCODE_ACKNOWLEDGE &&
+           ipath_cmp24(psn, qp->s_ack_psn) >= 0) {
+               if (qp->s_ack_state < IB_OPCODE_RDMA_READ_REQUEST)
+                       qp->s_ack_psn = psn;
+               spin_unlock(&qp->s_lock);
+               goto done;
+       }
+       switch (opcode) {
+       case OP(RDMA_READ_REQUEST):
+               /*
+                * We have to be careful to not change s_rdma_sge
+                * while ipath_do_rc_send() is using it and not
+                * holding the s_lock.
+                */
+               if (qp->s_ack_state != OP(ACKNOWLEDGE) &&
+                   qp->s_ack_state >= IB_OPCODE_RDMA_READ_REQUEST) {
+                       spin_unlock(&qp->s_lock);
+                       dev->n_rdma_dup_busy++;
+                       goto done;
+               }
+               /* RETH comes after BTH */
+               if (!header_in_data)
+                       reth = &ohdr->u.rc.reth;
+               else {
+                       reth = (struct ib_reth *)data;
+                       data += sizeof(*reth);
+               }
+               qp->s_rdma_len = be32_to_cpu(reth->length);
+               if (qp->s_rdma_len != 0) {
+                       u32 rkey = be32_to_cpu(reth->rkey);
+                       u64 vaddr = be64_to_cpu(reth->vaddr);
+                       int ok;
+
+                       /*
+                        * Address range must be a subset of the original
+                        * request and start on pmtu boundaries.
+                        */
+                       ok = ipath_rkey_ok(dev, &qp->s_rdma_sge,
+                                          qp->s_rdma_len, vaddr, rkey,
+                                          IB_ACCESS_REMOTE_READ);
+                       if (unlikely(!ok))
+                               goto done;
+               } else {
+                       qp->s_rdma_sge.sg_list = NULL;
+                       qp->s_rdma_sge.num_sge = 0;
+                       qp->s_rdma_sge.sge.mr = NULL;
+                       qp->s_rdma_sge.sge.vaddr = NULL;
+                       qp->s_rdma_sge.sge.length = 0;
+                       qp->s_rdma_sge.sge.sge_length = 0;
+               }
+               break;
+
+       case OP(COMPARE_SWAP):
+       case OP(FETCH_ADD):
+               /*
+                * Check for the PSN of the last atomic operations
+                * performed and resend the result if found.
+                */
+               if ((psn & IPS_PSN_MASK) != qp->r_atomic_psn) {
+                       spin_unlock(&qp->s_lock);
+                       goto done;
+               }
+               qp->s_ack_atomic = qp->r_atomic_data;
+               break;
+       }
+       qp->s_ack_state = opcode;
+       qp->s_nak_state = 0;
+       qp->s_ack_psn = psn;
+resched:
+       return 0;
+
+done:
+       return 1;
+}
+
+/**
+ * ipath_rc_rcv - process an incoming RC packet
+ * @dev: the device this packet came in on
+ * @hdr: the header of this packet
+ * @has_grh: true if the header has a GRH
+ * @data: the packet data
+ * @tlen: the packet length
+ * @qp: the QP for this packet
+ *
+ * This is called from ipath_qp_rcv() to process an incoming RC packet
+ * for the given QP.
+ * Called at interrupt level.
+ */
+void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
+                 int has_grh, void *data, u32 tlen, struct ipath_qp *qp)
+{
+       struct ipath_other_headers *ohdr;
+       u32 opcode;
+       u32 hdrsize;
+       u32 psn;
+       u32 pad;
+       unsigned long flags;
+       struct ib_wc wc;
+       u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+       int diff;
+       struct ib_reth *reth;
+       int header_in_data;
+
+       /* Check for GRH */
+       if (!has_grh) {
+               ohdr = &hdr->u.oth;
+               hdrsize = 8 + 12;       /* LRH + BTH */
+               psn = be32_to_cpu(ohdr->bth[2]);
+               header_in_data = 0;
+       } else {
+               ohdr = &hdr->u.l.oth;
+               hdrsize = 8 + 40 + 12;  /* LRH + GRH + BTH */
+               /*
+                * The header with GRH is 60 bytes and the core driver sets
+                * the eager header buffer size to 56 bytes so the last 4
+                * bytes of the BTH header (PSN) is in the data buffer.
+                */
+               header_in_data =
+                       ipath_layer_get_rcvhdrentsize(dev->dd) == 16;
+               if (header_in_data) {
+                       psn = be32_to_cpu(((__be32 *) data)[0]);
+                       data += sizeof(__be32);
+               } else
+                       psn = be32_to_cpu(ohdr->bth[2]);
+       }
+       /*
+        * The opcode is in the low byte when its in network order
+        * (top byte when in host order).
+        */
+       opcode = be32_to_cpu(ohdr->bth[0]) >> 24;
+
+       /*
+        * Process responses (ACKs) before anything else.  Note that the
+        * packet sequence number will be for something in the send work
+        * queue rather than the expected receive packet sequence number.
+        * In other words, this QP is the requester.
+        */
+       if (opcode >= OP(RDMA_READ_RESPONSE_FIRST) &&
+           opcode <= OP(ATOMIC_ACKNOWLEDGE)) {
+               ipath_rc_rcv_resp(dev, ohdr, data, tlen, qp, opcode, psn,
+                                 hdrsize, pmtu, header_in_data);
+               goto bail;
+       }
+
+       spin_lock_irqsave(&qp->r_rq.lock, flags);
+
+       /* Compute 24 bits worth of difference. */
+       diff = ipath_cmp24(psn, qp->r_psn);
+       if (unlikely(diff)) {
+               if (ipath_rc_rcv_error(dev, ohdr, data, qp, opcode,
+                                      psn, diff, header_in_data))
+                       goto done;
+               goto resched;
+       }
+
+       /* Check for opcode sequence errors. */
+       switch (qp->r_state) {
+       case OP(SEND_FIRST):
+       case OP(SEND_MIDDLE):
+               if (opcode == OP(SEND_MIDDLE) ||
+                   opcode == OP(SEND_LAST) ||
+                   opcode == OP(SEND_LAST_WITH_IMMEDIATE))
+                       break;
+       nack_inv:
+       /*
+        * A NAK will ACK earlier sends and RDMA writes.  Don't queue the
+        * NAK if a RDMA read, atomic, or NAK is pending though.
+        */
+       spin_lock(&qp->s_lock);
+       if (qp->s_ack_state >= OP(RDMA_READ_REQUEST) &&
+           qp->s_ack_state != IB_OPCODE_ACKNOWLEDGE) {
+               spin_unlock(&qp->s_lock);
+               goto done;
+       }
+       /* XXX Flush WQEs */
+       qp->state = IB_QPS_ERR;
+       qp->s_ack_state = OP(SEND_ONLY);
+       qp->s_nak_state = IB_NAK_INVALID_REQUEST;
+       qp->s_ack_psn = qp->r_psn;
+       goto resched;
+
+       case OP(RDMA_WRITE_FIRST):
+       case OP(RDMA_WRITE_MIDDLE):
+               if (opcode == OP(RDMA_WRITE_MIDDLE) ||
+                   opcode == OP(RDMA_WRITE_LAST) ||
+                   opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE))
+                       break;
+               goto nack_inv;
+
+       case OP(RDMA_READ_REQUEST):
+       case OP(COMPARE_SWAP):
+       case OP(FETCH_ADD):
+               /*
+                * Drop all new requests until a response has been sent.  A
+                * new request then ACKs the RDMA response we sent.  Relaxed
+                * ordering would allow new requests to be processed but we
+                * would need to keep a queue of rwqe's for all that are in
+                * progress.  Note that we can't RNR NAK this request since
+                * the RDMA READ or atomic response is already queued to be
+                * sent (unless we implement a response send queue).
+                */
+               goto done;
+
+       default:
+               if (opcode == OP(SEND_MIDDLE) ||
+                   opcode == OP(SEND_LAST) ||
+                   opcode == OP(SEND_LAST_WITH_IMMEDIATE) ||
+                   opcode == OP(RDMA_WRITE_MIDDLE) ||
+                   opcode == OP(RDMA_WRITE_LAST) ||
+                   opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE))
+                       goto nack_inv;
+               break;
+       }
+
+       wc.imm_data = 0;
+       wc.wc_flags = 0;
+
+       /* OK, process the packet. */
+       switch (opcode) {
+       case OP(SEND_FIRST):
+               if (!ipath_get_rwqe(qp, 0)) {
+               rnr_nak:
+                       /*
+                        * A RNR NAK will ACK earlier sends and RDMA writes.
+                        * Don't queue the NAK if a RDMA read or atomic
+                        * is pending though.
+                        */
+                       spin_lock(&qp->s_lock);
+                       if (qp->s_ack_state >=
+                           OP(RDMA_READ_REQUEST) &&
+                           qp->s_ack_state != IB_OPCODE_ACKNOWLEDGE) {
+                               spin_unlock(&qp->s_lock);
+                               goto done;
+                       }
+                       qp->s_ack_state = OP(SEND_ONLY);
+                       qp->s_nak_state = IB_RNR_NAK | qp->s_min_rnr_timer;
+                       qp->s_ack_psn = qp->r_psn;
+                       goto resched;
+               }
+               qp->r_rcv_len = 0;
+               /* FALLTHROUGH */
+       case OP(SEND_MIDDLE):
+       case OP(RDMA_WRITE_MIDDLE):
+       send_middle:
+               /* Check for invalid length PMTU or posted rwqe len. */
+               if (unlikely(tlen != (hdrsize + pmtu + 4)))
+                       goto nack_inv;
+               qp->r_rcv_len += pmtu;
+               if (unlikely(qp->r_rcv_len > qp->r_len))
+                       goto nack_inv;
+               ipath_copy_sge(&qp->r_sge, data, pmtu);
+               break;
+
+       case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE):
+               /* consume RWQE */
+               if (!ipath_get_rwqe(qp, 1))
+                       goto rnr_nak;
+               goto send_last_imm;
+
+       case OP(SEND_ONLY):
+       case OP(SEND_ONLY_WITH_IMMEDIATE):
+               if (!ipath_get_rwqe(qp, 0))
+                       goto rnr_nak;
+               qp->r_rcv_len = 0;
+               if (opcode == OP(SEND_ONLY))
+                       goto send_last;
+               /* FALLTHROUGH */
+       case OP(SEND_LAST_WITH_IMMEDIATE):
+       send_last_imm:
+               if (header_in_data) {
+                       wc.imm_data = *(__be32 *) data;
+                       data += sizeof(__be32);
+               } else {
+                       /* Immediate data comes after BTH */
+                       wc.imm_data = ohdr->u.imm_data;
+               }
+               hdrsize += 4;
+               wc.wc_flags = IB_WC_WITH_IMM;
+               /* FALLTHROUGH */
+       case OP(SEND_LAST):
+       case OP(RDMA_WRITE_LAST):
+       send_last:
+               /* Get the number of bytes the message was padded by. */
+               pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
+               /* Check for invalid length. */
+               /* XXX LAST len should be >= 1 */
+               if (unlikely(tlen < (hdrsize + pad + 4)))
+                       goto nack_inv;
+               /* Don't count the CRC. */
+               tlen -= (hdrsize + pad + 4);
+               wc.byte_len = tlen + qp->r_rcv_len;
+               if (unlikely(wc.byte_len > qp->r_len))
+                       goto nack_inv;
+               ipath_copy_sge(&qp->r_sge, data, tlen);
+               atomic_inc(&qp->msn);
+               if (opcode == OP(RDMA_WRITE_LAST) ||
+                   opcode == OP(RDMA_WRITE_ONLY))
+                       break;
+               wc.wr_id = qp->r_wr_id;
+               wc.status = IB_WC_SUCCESS;
+               wc.opcode = IB_WC_RECV;
+               wc.vendor_err = 0;
+               wc.qp_num = qp->ibqp.qp_num;
+               wc.src_qp = qp->remote_qpn;
+               wc.pkey_index = 0;
+               wc.slid = qp->remote_ah_attr.dlid;
+               wc.sl = qp->remote_ah_attr.sl;
+               wc.dlid_path_bits = 0;
+               wc.port_num = 0;
+               /* Signal completion event if the solicited bit is set. */
+               ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
+                              (ohdr->bth[0] &
+                               __constant_cpu_to_be32(1 << 23)) != 0);
+               break;
+
+       case OP(RDMA_WRITE_FIRST):
+       case OP(RDMA_WRITE_ONLY):
+       case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE):
+               /* consume RWQE */
+               /* RETH comes after BTH */
+               if (!header_in_data)
+                       reth = &ohdr->u.rc.reth;
+               else {
+                       reth = (struct ib_reth *)data;
+                       data += sizeof(*reth);
+               }
+               hdrsize += sizeof(*reth);
+               qp->r_len = be32_to_cpu(reth->length);
+               qp->r_rcv_len = 0;
+               if (qp->r_len != 0) {
+                       u32 rkey = be32_to_cpu(reth->rkey);
+                       u64 vaddr = be64_to_cpu(reth->vaddr);
+                       int ok;
+
+                       /* Check rkey & NAK */
+                       ok = ipath_rkey_ok(dev, &qp->r_sge,
+                                          qp->r_len, vaddr, rkey,
+                                          IB_ACCESS_REMOTE_WRITE);
+                       if (unlikely(!ok)) {
+                       nack_acc:
+                               /*
+                                * A NAK will ACK earlier sends and RDMA
+                                * writes.  Don't queue the NAK if a RDMA
+                                * read, atomic, or NAK is pending though.
+                                */
+                               spin_lock(&qp->s_lock);
+                               if (qp->s_ack_state >=
+                                   OP(RDMA_READ_REQUEST) &&
+                                   qp->s_ack_state !=
+                                   IB_OPCODE_ACKNOWLEDGE) {
+                                       spin_unlock(&qp->s_lock);
+                                       goto done;
+                               }
+                               /* XXX Flush WQEs */
+                               qp->state = IB_QPS_ERR;
+                               qp->s_ack_state = OP(RDMA_WRITE_ONLY);
+                               qp->s_nak_state =
+                                       IB_NAK_REMOTE_ACCESS_ERROR;
+                               qp->s_ack_psn = qp->r_psn;
+                               goto resched;
+                       }
+               } else {
+                       qp->r_sge.sg_list = NULL;
+                       qp->r_sge.sge.mr = NULL;
+                       qp->r_sge.sge.vaddr = NULL;
+                       qp->r_sge.sge.length = 0;
+                       qp->r_sge.sge.sge_length = 0;
+               }
+               if (unlikely(!(qp->qp_access_flags &
+                              IB_ACCESS_REMOTE_WRITE)))
+                       goto nack_acc;
+               if (opcode == OP(RDMA_WRITE_FIRST))
+                       goto send_middle;
+               else if (opcode == OP(RDMA_WRITE_ONLY))
+                       goto send_last;
+               if (!ipath_get_rwqe(qp, 1))
+                       goto rnr_nak;
+               goto send_last_imm;
+
+       case OP(RDMA_READ_REQUEST):
+               /* RETH comes after BTH */
+               if (!header_in_data)
+                       reth = &ohdr->u.rc.reth;
+               else {
+                       reth = (struct ib_reth *)data;
+                       data += sizeof(*reth);
+               }
+               spin_lock(&qp->s_lock);
+               if (qp->s_ack_state != OP(ACKNOWLEDGE) &&
+                   qp->s_ack_state >= IB_OPCODE_RDMA_READ_REQUEST) {
+                       spin_unlock(&qp->s_lock);
+                       goto done;
+               }
+               qp->s_rdma_len = be32_to_cpu(reth->length);
+               if (qp->s_rdma_len != 0) {
+                       u32 rkey = be32_to_cpu(reth->rkey);
+                       u64 vaddr = be64_to_cpu(reth->vaddr);
+                       int ok;
+
+                       /* Check rkey & NAK */
+                       ok = ipath_rkey_ok(dev, &qp->s_rdma_sge,
+                                          qp->s_rdma_len, vaddr, rkey,
+                                          IB_ACCESS_REMOTE_READ);
+                       if (unlikely(!ok)) {
+                               spin_unlock(&qp->s_lock);
+                               goto nack_acc;
+                       }
+                       /*
+                        * Update the next expected PSN.  We add 1 later
+                        * below, so only add the remainder here.
+                        */
+                       if (qp->s_rdma_len > pmtu)
+                               qp->r_psn += (qp->s_rdma_len - 1) / pmtu;
+               } else {
+                       qp->s_rdma_sge.sg_list = NULL;
+                       qp->s_rdma_sge.num_sge = 0;
+                       qp->s_rdma_sge.sge.mr = NULL;
+                       qp->s_rdma_sge.sge.vaddr = NULL;
+                       qp->s_rdma_sge.sge.length = 0;
+                       qp->s_rdma_sge.sge.sge_length = 0;
+               }
+               if (unlikely(!(qp->qp_access_flags &
+                              IB_ACCESS_REMOTE_READ)))
+                       goto nack_acc;
+               /*
+                * We need to increment the MSN here instead of when we
+                * finish sending the result since a duplicate request would
+                * increment it more than once.
+                */
+               atomic_inc(&qp->msn);
+               qp->s_ack_state = opcode;
+               qp->s_nak_state = 0;
+               qp->s_ack_psn = psn;
+               qp->r_psn++;
+               qp->r_state = opcode;
+               goto rdmadone;
+
+       case OP(COMPARE_SWAP):
+       case OP(FETCH_ADD): {
+               struct ib_atomic_eth *ateth;
+               u64 vaddr;
+               u64 sdata;
+               u32 rkey;
+
+               if (!header_in_data)
+                       ateth = &ohdr->u.atomic_eth;
+               else {
+                       ateth = (struct ib_atomic_eth *)data;
+                       data += sizeof(*ateth);
+               }
+               vaddr = be64_to_cpu(ateth->vaddr);
+               if (unlikely(vaddr & (sizeof(u64) - 1)))
+                       goto nack_inv;
+               rkey = be32_to_cpu(ateth->rkey);
+               /* Check rkey & NAK */
+               if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge,
+                                           sizeof(u64), vaddr, rkey,
+                                           IB_ACCESS_REMOTE_ATOMIC)))
+                       goto nack_acc;
+               if (unlikely(!(qp->qp_access_flags &
+                              IB_ACCESS_REMOTE_ATOMIC)))
+                       goto nack_acc;
+               /* Perform atomic OP and save result. */
+               sdata = be64_to_cpu(ateth->swap_data);
+               spin_lock(&dev->pending_lock);
+               qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr;
+               if (opcode == OP(FETCH_ADD))
+                       *(u64 *) qp->r_sge.sge.vaddr =
+                               qp->r_atomic_data + sdata;
+               else if (qp->r_atomic_data ==
+                        be64_to_cpu(ateth->compare_data))
+                       *(u64 *) qp->r_sge.sge.vaddr = sdata;
+               spin_unlock(&dev->pending_lock);
+               atomic_inc(&qp->msn);
+               qp->r_atomic_psn = psn & IPS_PSN_MASK;
+               psn |= 1 << 31;
+               break;
+       }
+
+       default:
+               /* Drop packet for unknown opcodes. */
+               goto done;
+       }
+       qp->r_psn++;
+       qp->r_state = opcode;
+       /* Send an ACK if requested or required. */
+       if (psn & (1 << 31)) {
+               /*
+                * Coalesce ACKs unless there is a RDMA READ or
+                * ATOMIC pending.
+                */
+               spin_lock(&qp->s_lock);
+               if (qp->s_ack_state == OP(ACKNOWLEDGE) ||
+                   qp->s_ack_state < IB_OPCODE_RDMA_READ_REQUEST) {
+                       qp->s_ack_state = opcode;
+                       qp->s_nak_state = 0;
+                       qp->s_ack_psn = psn;
+                       qp->s_ack_atomic = qp->r_atomic_data;
+                       goto resched;
+               }
+               spin_unlock(&qp->s_lock);
+       }
+done:
+       spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+       goto bail;
+
+resched:
+       /*
+        * Try to send ACK right away but not if ipath_do_rc_send() is
+        * active.
+        */
+       if (qp->s_hdrwords == 0 &&
+           (qp->s_ack_state < IB_OPCODE_RDMA_READ_REQUEST ||
+            qp->s_ack_state >= IB_OPCODE_COMPARE_SWAP))
+               send_rc_ack(qp);
+
+rdmadone:
+       spin_unlock(&qp->s_lock);
+       spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+
+       /* Call ipath_do_rc_send() in another thread. */
+       tasklet_hi_schedule(&qp->s_task);
+
+bail:
+       return;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_registers.h b/drivers/infiniband/hw/ipath/ipath_registers.h
new file mode 100644 (file)
index 0000000..1e59750
--- /dev/null
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _IPATH_REGISTERS_H
+#define _IPATH_REGISTERS_H
+
+/*
+ * This file should only be included by kernel source, and by the diags.
+ * It defines the registers, and their contents, for the InfiniPath HT-400 chip
+ */
+
+/*
+ * These are the InfiniPath register and buffer bit definitions,
+ * that are visible to software, and needed only by the kernel
+ * and diag code.  A few, that are visible to protocol and user
+ * code are in ipath_common.h.  Some bits are specific
+ * to a given chip implementation, and have been moved to the
+ * chip-specific source file
+ */
+
+/* kr_revision bits */
+#define INFINIPATH_R_CHIPREVMINOR_MASK 0xFF
+#define INFINIPATH_R_CHIPREVMINOR_SHIFT 0
+#define INFINIPATH_R_CHIPREVMAJOR_MASK 0xFF
+#define INFINIPATH_R_CHIPREVMAJOR_SHIFT 8
+#define INFINIPATH_R_ARCH_MASK 0xFF
+#define INFINIPATH_R_ARCH_SHIFT 16
+#define INFINIPATH_R_SOFTWARE_MASK 0xFF
+#define INFINIPATH_R_SOFTWARE_SHIFT 24
+#define INFINIPATH_R_BOARDID_MASK 0xFF
+#define INFINIPATH_R_BOARDID_SHIFT 32
+
+/* kr_control bits */
+#define INFINIPATH_C_FREEZEMODE 0x00000002
+#define INFINIPATH_C_LINKENABLE 0x00000004
+#define INFINIPATH_C_RESET 0x00000001
+
+/* kr_sendctrl bits */
+#define INFINIPATH_S_DISARMPIOBUF_SHIFT 16
+
+#define IPATH_S_ABORT          0
+#define IPATH_S_PIOINTBUFAVAIL 1
+#define IPATH_S_PIOBUFAVAILUPD 2
+#define IPATH_S_PIOENABLE      3
+#define IPATH_S_DISARM         31
+
+#define INFINIPATH_S_ABORT             (1U << IPATH_S_ABORT)
+#define INFINIPATH_S_PIOINTBUFAVAIL    (1U << IPATH_S_PIOINTBUFAVAIL)
+#define INFINIPATH_S_PIOBUFAVAILUPD    (1U << IPATH_S_PIOBUFAVAILUPD)
+#define INFINIPATH_S_PIOENABLE         (1U << IPATH_S_PIOENABLE)
+#define INFINIPATH_S_DISARM            (1U << IPATH_S_DISARM)
+
+/* kr_rcvctrl bits */
+#define INFINIPATH_R_PORTENABLE_SHIFT 0
+#define INFINIPATH_R_INTRAVAIL_SHIFT 16
+#define INFINIPATH_R_TAILUPD   0x80000000
+
+/* kr_intstatus, kr_intclear, kr_intmask bits */
+#define INFINIPATH_I_RCVURG_SHIFT 0
+#define INFINIPATH_I_RCVAVAIL_SHIFT 12
+#define INFINIPATH_I_ERROR        0x80000000
+#define INFINIPATH_I_SPIOSENT     0x40000000
+#define INFINIPATH_I_SPIOBUFAVAIL 0x20000000
+#define INFINIPATH_I_GPIO         0x10000000
+
+/* kr_errorstatus, kr_errorclear, kr_errormask bits */
+#define INFINIPATH_E_RFORMATERR      0x0000000000000001ULL
+#define INFINIPATH_E_RVCRC           0x0000000000000002ULL
+#define INFINIPATH_E_RICRC           0x0000000000000004ULL
+#define INFINIPATH_E_RMINPKTLEN      0x0000000000000008ULL
+#define INFINIPATH_E_RMAXPKTLEN      0x0000000000000010ULL
+#define INFINIPATH_E_RLONGPKTLEN     0x0000000000000020ULL
+#define INFINIPATH_E_RSHORTPKTLEN    0x0000000000000040ULL
+#define INFINIPATH_E_RUNEXPCHAR      0x0000000000000080ULL
+#define INFINIPATH_E_RUNSUPVL        0x0000000000000100ULL
+#define INFINIPATH_E_REBP            0x0000000000000200ULL
+#define INFINIPATH_E_RIBFLOW         0x0000000000000400ULL
+#define INFINIPATH_E_RBADVERSION     0x0000000000000800ULL
+#define INFINIPATH_E_RRCVEGRFULL     0x0000000000001000ULL
+#define INFINIPATH_E_RRCVHDRFULL     0x0000000000002000ULL
+#define INFINIPATH_E_RBADTID         0x0000000000004000ULL
+#define INFINIPATH_E_RHDRLEN         0x0000000000008000ULL
+#define INFINIPATH_E_RHDR            0x0000000000010000ULL
+#define INFINIPATH_E_RIBLOSTLINK     0x0000000000020000ULL
+#define INFINIPATH_E_SMINPKTLEN      0x0000000020000000ULL
+#define INFINIPATH_E_SMAXPKTLEN      0x0000000040000000ULL
+#define INFINIPATH_E_SUNDERRUN       0x0000000080000000ULL
+#define INFINIPATH_E_SPKTLEN         0x0000000100000000ULL
+#define INFINIPATH_E_SDROPPEDSMPPKT  0x0000000200000000ULL
+#define INFINIPATH_E_SDROPPEDDATAPKT 0x0000000400000000ULL
+#define INFINIPATH_E_SPIOARMLAUNCH   0x0000000800000000ULL
+#define INFINIPATH_E_SUNEXPERRPKTNUM 0x0000001000000000ULL
+#define INFINIPATH_E_SUNSUPVL        0x0000002000000000ULL
+#define INFINIPATH_E_IBSTATUSCHANGED 0x0001000000000000ULL
+#define INFINIPATH_E_INVALIDADDR     0x0002000000000000ULL
+#define INFINIPATH_E_RESET           0x0004000000000000ULL
+#define INFINIPATH_E_HARDWARE        0x0008000000000000ULL
+
+/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
+/* TXEMEMPARITYERR bit 0: PIObuf, 1: PIOpbc, 2: launchfifo
+ * RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2: eagerTID, 3: expTID
+ *             bit 4: flag buffer, 5: datainfo, 6: header info */
+#define INFINIPATH_HWE_TXEMEMPARITYERR_MASK 0xFULL
+#define INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT 40
+#define INFINIPATH_HWE_RXEMEMPARITYERR_MASK 0x7FULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT 44
+#define INFINIPATH_HWE_RXDSYNCMEMPARITYERR  0x0000000400000000ULL
+#define INFINIPATH_HWE_MEMBISTFAILED        0x0040000000000000ULL
+#define INFINIPATH_HWE_IBCBUSTOSPCPARITYERR 0x4000000000000000ULL
+#define INFINIPATH_HWE_IBCBUSFRSPCPARITYERR 0x8000000000000000ULL
+
+/* kr_hwdiagctrl bits */
+#define INFINIPATH_DC_FORCETXEMEMPARITYERR_MASK 0xFULL
+#define INFINIPATH_DC_FORCETXEMEMPARITYERR_SHIFT 40
+#define INFINIPATH_DC_FORCERXEMEMPARITYERR_MASK 0x7FULL
+#define INFINIPATH_DC_FORCERXEMEMPARITYERR_SHIFT 44
+#define INFINIPATH_DC_FORCERXDSYNCMEMPARITYERR  0x0000000400000000ULL
+#define INFINIPATH_DC_COUNTERDISABLE            0x1000000000000000ULL
+#define INFINIPATH_DC_COUNTERWREN               0x2000000000000000ULL
+#define INFINIPATH_DC_FORCEIBCBUSTOSPCPARITYERR 0x4000000000000000ULL
+#define INFINIPATH_DC_FORCEIBCBUSFRSPCPARITYERR 0x8000000000000000ULL
+
+/* kr_ibcctrl bits */
+#define INFINIPATH_IBCC_FLOWCTRLPERIOD_MASK 0xFFULL
+#define INFINIPATH_IBCC_FLOWCTRLPERIOD_SHIFT 0
+#define INFINIPATH_IBCC_FLOWCTRLWATERMARK_MASK 0xFFULL
+#define INFINIPATH_IBCC_FLOWCTRLWATERMARK_SHIFT 8
+#define INFINIPATH_IBCC_LINKINITCMD_MASK 0x3ULL
+#define INFINIPATH_IBCC_LINKINITCMD_DISABLE 1
+#define INFINIPATH_IBCC_LINKINITCMD_POLL 2     /* cycle through TS1/TS2 till OK */
+#define INFINIPATH_IBCC_LINKINITCMD_SLEEP 3    /* wait for TS1, then go on */
+#define INFINIPATH_IBCC_LINKINITCMD_SHIFT 16
+#define INFINIPATH_IBCC_LINKCMD_MASK 0x3ULL
+#define INFINIPATH_IBCC_LINKCMD_INIT 1 /* move to 0x11 */
+#define INFINIPATH_IBCC_LINKCMD_ARMED 2        /* move to 0x21 */
+#define INFINIPATH_IBCC_LINKCMD_ACTIVE 3       /* move to 0x31 */
+#define INFINIPATH_IBCC_LINKCMD_SHIFT 18
+#define INFINIPATH_IBCC_MAXPKTLEN_MASK 0x7FFULL
+#define INFINIPATH_IBCC_MAXPKTLEN_SHIFT 20
+#define INFINIPATH_IBCC_PHYERRTHRESHOLD_MASK 0xFULL
+#define INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT 32
+#define INFINIPATH_IBCC_OVERRUNTHRESHOLD_MASK 0xFULL
+#define INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT 36
+#define INFINIPATH_IBCC_CREDITSCALE_MASK 0x7ULL
+#define INFINIPATH_IBCC_CREDITSCALE_SHIFT 40
+#define INFINIPATH_IBCC_LOOPBACK             0x8000000000000000ULL
+#define INFINIPATH_IBCC_LINKDOWNDEFAULTSTATE 0x4000000000000000ULL
+
+/* kr_ibcstatus bits */
+#define INFINIPATH_IBCS_LINKTRAININGSTATE_MASK 0xF
+#define INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT 0
+#define INFINIPATH_IBCS_LINKSTATE_MASK 0x7
+#define INFINIPATH_IBCS_LINKSTATE_SHIFT 4
+#define INFINIPATH_IBCS_TXREADY       0x40000000
+#define INFINIPATH_IBCS_TXCREDITOK    0x80000000
+/* link training states (shift by INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) */
+#define INFINIPATH_IBCS_LT_STATE_DISABLED      0x00
+#define INFINIPATH_IBCS_LT_STATE_LINKUP                0x01
+#define INFINIPATH_IBCS_LT_STATE_POLLACTIVE    0x02
+#define INFINIPATH_IBCS_LT_STATE_POLLQUIET     0x03
+#define INFINIPATH_IBCS_LT_STATE_SLEEPDELAY    0x04
+#define INFINIPATH_IBCS_LT_STATE_SLEEPQUIET    0x05
+#define INFINIPATH_IBCS_LT_STATE_CFGDEBOUNCE   0x08
+#define INFINIPATH_IBCS_LT_STATE_CFGRCVFCFG    0x09
+#define INFINIPATH_IBCS_LT_STATE_CFGWAITRMT    0x0a
+#define INFINIPATH_IBCS_LT_STATE_CFGIDLE       0x0b
+#define INFINIPATH_IBCS_LT_STATE_RECOVERRETRAIN        0x0c
+#define INFINIPATH_IBCS_LT_STATE_RECOVERWAITRMT        0x0e
+#define INFINIPATH_IBCS_LT_STATE_RECOVERIDLE   0x0f
+/* link state machine states (shift by INFINIPATH_IBCS_LINKSTATE_SHIFT) */
+#define INFINIPATH_IBCS_L_STATE_DOWN           0x0
+#define INFINIPATH_IBCS_L_STATE_INIT           0x1
+#define INFINIPATH_IBCS_L_STATE_ARM            0x2
+#define INFINIPATH_IBCS_L_STATE_ACTIVE         0x3
+#define INFINIPATH_IBCS_L_STATE_ACT_DEFER      0x4
+
+/* combination link status states that we use with some frequency */
+#define IPATH_IBSTATE_MASK ((INFINIPATH_IBCS_LINKTRAININGSTATE_MASK \
+               << INFINIPATH_IBCS_LINKSTATE_SHIFT) | \
+               (INFINIPATH_IBCS_LINKSTATE_MASK \
+               <<INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT))
+#define IPATH_IBSTATE_INIT ((INFINIPATH_IBCS_L_STATE_INIT \
+               << INFINIPATH_IBCS_LINKSTATE_SHIFT) | \
+               (INFINIPATH_IBCS_LT_STATE_LINKUP \
+               <<INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT))
+#define IPATH_IBSTATE_ARM ((INFINIPATH_IBCS_L_STATE_ARM \
+               << INFINIPATH_IBCS_LINKSTATE_SHIFT) | \
+               (INFINIPATH_IBCS_LT_STATE_LINKUP \
+               <<INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT))
+#define IPATH_IBSTATE_ACTIVE ((INFINIPATH_IBCS_L_STATE_ACTIVE \
+               << INFINIPATH_IBCS_LINKSTATE_SHIFT) | \
+               (INFINIPATH_IBCS_LT_STATE_LINKUP \
+               <<INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT))
+
+/* kr_extstatus bits */
+#define INFINIPATH_EXTS_SERDESPLLLOCK 0x1
+#define INFINIPATH_EXTS_GPIOIN_MASK 0xFFFFULL
+#define INFINIPATH_EXTS_GPIOIN_SHIFT 48
+
+/* kr_extctrl bits */
+#define INFINIPATH_EXTC_GPIOINVERT_MASK 0xFFFFULL
+#define INFINIPATH_EXTC_GPIOINVERT_SHIFT 32
+#define INFINIPATH_EXTC_GPIOOE_MASK 0xFFFFULL
+#define INFINIPATH_EXTC_GPIOOE_SHIFT 48
+#define INFINIPATH_EXTC_SERDESENABLE         0x80000000ULL
+#define INFINIPATH_EXTC_SERDESCONNECT        0x40000000ULL
+#define INFINIPATH_EXTC_SERDESENTRUNKING     0x20000000ULL
+#define INFINIPATH_EXTC_SERDESDISRXFIFO      0x10000000ULL
+#define INFINIPATH_EXTC_SERDESENPLPBK1       0x08000000ULL
+#define INFINIPATH_EXTC_SERDESENPLPBK2       0x04000000ULL
+#define INFINIPATH_EXTC_SERDESENENCDEC       0x02000000ULL
+#define INFINIPATH_EXTC_LED1SECPORT_ON       0x00000020ULL
+#define INFINIPATH_EXTC_LED2SECPORT_ON       0x00000010ULL
+#define INFINIPATH_EXTC_LED1PRIPORT_ON       0x00000008ULL
+#define INFINIPATH_EXTC_LED2PRIPORT_ON       0x00000004ULL
+#define INFINIPATH_EXTC_LEDGBLOK_ON          0x00000002ULL
+#define INFINIPATH_EXTC_LEDGBLERR_OFF        0x00000001ULL
+
+/* kr_mdio bits */
+#define INFINIPATH_MDIO_CLKDIV_MASK 0x7FULL
+#define INFINIPATH_MDIO_CLKDIV_SHIFT 32
+#define INFINIPATH_MDIO_COMMAND_MASK 0x7ULL
+#define INFINIPATH_MDIO_COMMAND_SHIFT 26
+#define INFINIPATH_MDIO_DEVADDR_MASK 0x1FULL
+#define INFINIPATH_MDIO_DEVADDR_SHIFT 21
+#define INFINIPATH_MDIO_REGADDR_MASK 0x1FULL
+#define INFINIPATH_MDIO_REGADDR_SHIFT 16
+#define INFINIPATH_MDIO_DATA_MASK 0xFFFFULL
+#define INFINIPATH_MDIO_DATA_SHIFT 0
+#define INFINIPATH_MDIO_CMDVALID    0x0000000040000000ULL
+#define INFINIPATH_MDIO_RDDATAVALID 0x0000000080000000ULL
+
+/* kr_partitionkey bits */
+#define INFINIPATH_PKEY_SIZE 16
+#define INFINIPATH_PKEY_MASK 0xFFFF
+#define INFINIPATH_PKEY_DEFAULT_PKEY 0xFFFF
+
+/* kr_serdesconfig0 bits */
+#define INFINIPATH_SERDC0_RESET_MASK  0xfULL   /* overal reset bits */
+#define INFINIPATH_SERDC0_RESET_PLL   0x10000000ULL    /* pll reset */
+#define INFINIPATH_SERDC0_TXIDLE      0xF000ULL        /* tx idle enables (per lane) */
+#define INFINIPATH_SERDC0_RXDETECT_EN 0xF0000ULL       /* rx detect enables (per lane) */
+#define INFINIPATH_SERDC0_L1PWR_DN      0xF0ULL        /* L1 Power down; use with RXDETECT,
+                                                          Otherwise not used on IB side */
+
+/* kr_xgxsconfig bits */
+#define INFINIPATH_XGXS_RESET          0x7ULL
+#define INFINIPATH_XGXS_MDIOADDR_MASK  0xfULL
+#define INFINIPATH_XGXS_MDIOADDR_SHIFT 4
+
+#define INFINIPATH_RT_ADDR_MASK 0xFFFFFFFFFFULL        /* 40 bits valid */
+
+/* TID entries (memory), HT400-only */
+#define INFINIPATH_RT_VALID 0x8000000000000000ULL
+#define INFINIPATH_RT_ADDR_SHIFT 0
+#define INFINIPATH_RT_BUFSIZE_MASK 0x3FFF
+#define INFINIPATH_RT_BUFSIZE_SHIFT 48
+
+/*
+ * IPATH_PIO_MAXIBHDR is the max IB header size allowed for in our
+ * PIO send buffers.  This is well beyond anything currently
+ * defined in the InfiniBand spec.
+ */
+#define IPATH_PIO_MAXIBHDR 128
+
+typedef u64 ipath_err_t;
+
+/* mask of defined bits for various registers */
+extern u64 infinipath_i_bitsextant;
+extern ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant;
+
+/* masks that are different in various chips, or only exist in some chips */
+extern u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask;
+
+/*
+ * register bits for selecting i2c direction and values, used for I2C serial
+ * flash
+ */
+extern u16 ipath_gpio_sda_num, ipath_gpio_scl_num;
+extern u64 ipath_gpio_sda, ipath_gpio_scl;
+
+/*
+ * These are the infinipath general register numbers (not offsets).
+ * The kernel registers are used directly, those beyond the kernel
+ * registers are calculated from one of the base registers.  The use of
+ * an integer type doesn't allow type-checking as thorough as, say,
+ * an enum but allows for better hiding of chip differences.
+ */
+typedef const u16 ipath_kreg,  /* infinipath general registers */
+ ipath_creg,                   /* infinipath counter registers */
+ ipath_sreg;                   /* kernel-only, infinipath send registers */
+
+/*
+ * These are the chip registers common to all infinipath chips, and
+ * used both by the kernel and the diagnostics or other user code.
+ * They are all implemented such that 64 bit accesses work.
+ * Some implement no more than 32 bits.  Because 64 bit reads
+ * require 2 HT cmds on opteron, we access those with 32 bit
+ * reads for efficiency (they are written as 64 bits, since
+ * the extra 32 bits are nearly free on writes, and it slightly reduces
+ * complexity).  The rest are all accessed as 64 bits.
+ */
+struct ipath_kregs {
+       /* These are the 32 bit group */
+       ipath_kreg kr_control;
+       ipath_kreg kr_counterregbase;
+       ipath_kreg kr_intmask;
+       ipath_kreg kr_intstatus;
+       ipath_kreg kr_pagealign;
+       ipath_kreg kr_portcnt;
+       ipath_kreg kr_rcvtidbase;
+       ipath_kreg kr_rcvtidcnt;
+       ipath_kreg kr_rcvegrbase;
+       ipath_kreg kr_rcvegrcnt;
+       ipath_kreg kr_scratch;
+       ipath_kreg kr_sendctrl;
+       ipath_kreg kr_sendpiobufbase;
+       ipath_kreg kr_sendpiobufcnt;
+       ipath_kreg kr_sendpiosize;
+       ipath_kreg kr_sendregbase;
+       ipath_kreg kr_userregbase;
+       /* These are the 64 bit group */
+       ipath_kreg kr_debugport;
+       ipath_kreg kr_debugportselect;
+       ipath_kreg kr_errorclear;
+       ipath_kreg kr_errormask;
+       ipath_kreg kr_errorstatus;
+       ipath_kreg kr_extctrl;
+       ipath_kreg kr_extstatus;
+       ipath_kreg kr_gpio_clear;
+       ipath_kreg kr_gpio_mask;
+       ipath_kreg kr_gpio_out;
+       ipath_kreg kr_gpio_status;
+       ipath_kreg kr_hwdiagctrl;
+       ipath_kreg kr_hwerrclear;
+       ipath_kreg kr_hwerrmask;
+       ipath_kreg kr_hwerrstatus;
+       ipath_kreg kr_ibcctrl;
+       ipath_kreg kr_ibcstatus;
+       ipath_kreg kr_intblocked;
+       ipath_kreg kr_intclear;
+       ipath_kreg kr_interruptconfig;
+       ipath_kreg kr_mdio;
+       ipath_kreg kr_partitionkey;
+       ipath_kreg kr_rcvbthqp;
+       ipath_kreg kr_rcvbufbase;
+       ipath_kreg kr_rcvbufsize;
+       ipath_kreg kr_rcvctrl;
+       ipath_kreg kr_rcvhdrcnt;
+       ipath_kreg kr_rcvhdrentsize;
+       ipath_kreg kr_rcvhdrsize;
+       ipath_kreg kr_rcvintmembase;
+       ipath_kreg kr_rcvintmemsize;
+       ipath_kreg kr_revision;
+       ipath_kreg kr_sendbuffererror;
+       ipath_kreg kr_sendpioavailaddr;
+       ipath_kreg kr_serdesconfig0;
+       ipath_kreg kr_serdesconfig1;
+       ipath_kreg kr_serdesstatus;
+       ipath_kreg kr_txintmembase;
+       ipath_kreg kr_txintmemsize;
+       ipath_kreg kr_xgxsconfig;
+       ipath_kreg kr_ibpllcfg;
+       /* use these two (and the following N ports) only with ipath_k*_kreg64_port();
+        * not *kreg64() */
+       ipath_kreg kr_rcvhdraddr;
+       ipath_kreg kr_rcvhdrtailaddr;
+
+       /* remaining registers are not present on all types of infinipath chips  */
+       ipath_kreg kr_rcvpktledcnt;
+       ipath_kreg kr_pcierbuftestreg0;
+       ipath_kreg kr_pcierbuftestreg1;
+       ipath_kreg kr_pcieq0serdesconfig0;
+       ipath_kreg kr_pcieq0serdesconfig1;
+       ipath_kreg kr_pcieq0serdesstatus;
+       ipath_kreg kr_pcieq1serdesconfig0;
+       ipath_kreg kr_pcieq1serdesconfig1;
+       ipath_kreg kr_pcieq1serdesstatus;
+};
+
+struct ipath_cregs {
+       ipath_creg cr_badformatcnt;
+       ipath_creg cr_erricrccnt;
+       ipath_creg cr_errlinkcnt;
+       ipath_creg cr_errlpcrccnt;
+       ipath_creg cr_errpkey;
+       ipath_creg cr_errrcvflowctrlcnt;
+       ipath_creg cr_err_rlencnt;
+       ipath_creg cr_errslencnt;
+       ipath_creg cr_errtidfull;
+       ipath_creg cr_errtidvalid;
+       ipath_creg cr_errvcrccnt;
+       ipath_creg cr_ibstatuschange;
+       ipath_creg cr_intcnt;
+       ipath_creg cr_invalidrlencnt;
+       ipath_creg cr_invalidslencnt;
+       ipath_creg cr_lbflowstallcnt;
+       ipath_creg cr_iblinkdowncnt;
+       ipath_creg cr_iblinkerrrecovcnt;
+       ipath_creg cr_ibsymbolerrcnt;
+       ipath_creg cr_pktrcvcnt;
+       ipath_creg cr_pktrcvflowctrlcnt;
+       ipath_creg cr_pktsendcnt;
+       ipath_creg cr_pktsendflowcnt;
+       ipath_creg cr_portovflcnt;
+       ipath_creg cr_rcvebpcnt;
+       ipath_creg cr_rcvovflcnt;
+       ipath_creg cr_rxdroppktcnt;
+       ipath_creg cr_senddropped;
+       ipath_creg cr_sendstallcnt;
+       ipath_creg cr_sendunderruncnt;
+       ipath_creg cr_unsupvlcnt;
+       ipath_creg cr_wordrcvcnt;
+       ipath_creg cr_wordsendcnt;
+};
+
+#endif                         /* _IPATH_REGISTERS_H */
diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c
new file mode 100644 (file)
index 0000000..f232e77
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ipath_verbs.h"
+
+/*
+ * Convert the AETH RNR timeout code into the number of milliseconds.
+ */
+const u32 ib_ipath_rnr_table[32] = {
+       656,                    /* 0 */
+       1,                      /* 1 */
+       1,                      /* 2 */
+       1,                      /* 3 */
+       1,                      /* 4 */
+       1,                      /* 5 */
+       1,                      /* 6 */
+       1,                      /* 7 */
+       1,                      /* 8 */
+       1,                      /* 9 */
+       1,                      /* A */
+       1,                      /* B */
+       1,                      /* C */
+       1,                      /* D */
+       2,                      /* E */
+       2,                      /* F */
+       3,                      /* 10 */
+       4,                      /* 11 */
+       6,                      /* 12 */
+       8,                      /* 13 */
+       11,                     /* 14 */
+       16,                     /* 15 */
+       21,                     /* 16 */
+       31,                     /* 17 */
+       41,                     /* 18 */
+       62,                     /* 19 */
+       82,                     /* 1A */
+       123,                    /* 1B */
+       164,                    /* 1C */
+       246,                    /* 1D */
+       328,                    /* 1E */
+       492                     /* 1F */
+};
+
+/**
+ * ipath_insert_rnr_queue - put QP on the RNR timeout list for the device
+ * @qp: the QP
+ *
+ * XXX Use a simple list for now.  We might need a priority
+ * queue if we have lots of QPs waiting for RNR timeouts
+ * but that should be rare.
+ */
+void ipath_insert_rnr_queue(struct ipath_qp *qp)
+{
+       struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->pending_lock, flags);
+       if (list_empty(&dev->rnrwait))
+               list_add(&qp->timerwait, &dev->rnrwait);
+       else {
+               struct list_head *l = &dev->rnrwait;
+               struct ipath_qp *nqp = list_entry(l->next, struct ipath_qp,
+                                                 timerwait);
+
+               while (qp->s_rnr_timeout >= nqp->s_rnr_timeout) {
+                       qp->s_rnr_timeout -= nqp->s_rnr_timeout;
+                       l = l->next;
+                       if (l->next == &dev->rnrwait)
+                               break;
+                       nqp = list_entry(l->next, struct ipath_qp,
+                                        timerwait);
+               }
+               list_add(&qp->timerwait, l);
+       }
+       spin_unlock_irqrestore(&dev->pending_lock, flags);
+}
+
+/**
+ * ipath_get_rwqe - copy the next RWQE into the QP's RWQE
+ * @qp: the QP
+ * @wr_id_only: update wr_id only, not SGEs
+ *
+ * Return 0 if no RWQE is available, otherwise return 1.
+ *
+ * Called at interrupt level with the QP r_rq.lock held.
+ */
+int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only)
+{
+       struct ipath_rq *rq;
+       struct ipath_srq *srq;
+       struct ipath_rwqe *wqe;
+       int ret;
+
+       if (!qp->ibqp.srq) {
+               rq = &qp->r_rq;
+               if (unlikely(rq->tail == rq->head)) {
+                       ret = 0;
+                       goto bail;
+               }
+               wqe = get_rwqe_ptr(rq, rq->tail);
+               qp->r_wr_id = wqe->wr_id;
+               if (!wr_id_only) {
+                       qp->r_sge.sge = wqe->sg_list[0];
+                       qp->r_sge.sg_list = wqe->sg_list + 1;
+                       qp->r_sge.num_sge = wqe->num_sge;
+                       qp->r_len = wqe->length;
+               }
+               if (++rq->tail >= rq->size)
+                       rq->tail = 0;
+               ret = 1;
+               goto bail;
+       }
+
+       srq = to_isrq(qp->ibqp.srq);
+       rq = &srq->rq;
+       spin_lock(&rq->lock);
+       if (unlikely(rq->tail == rq->head)) {
+               spin_unlock(&rq->lock);
+               ret = 0;
+               goto bail;
+       }
+       wqe = get_rwqe_ptr(rq, rq->tail);
+       qp->r_wr_id = wqe->wr_id;
+       if (!wr_id_only) {
+               qp->r_sge.sge = wqe->sg_list[0];
+               qp->r_sge.sg_list = wqe->sg_list + 1;
+               qp->r_sge.num_sge = wqe->num_sge;
+               qp->r_len = wqe->length;
+       }
+       if (++rq->tail >= rq->size)
+               rq->tail = 0;
+       if (srq->ibsrq.event_handler) {
+               struct ib_event ev;
+               u32 n;
+
+               if (rq->head < rq->tail)
+                       n = rq->size + rq->head - rq->tail;
+               else
+                       n = rq->head - rq->tail;
+               if (n < srq->limit) {
+                       srq->limit = 0;
+                       spin_unlock(&rq->lock);
+                       ev.device = qp->ibqp.device;
+                       ev.element.srq = qp->ibqp.srq;
+                       ev.event = IB_EVENT_SRQ_LIMIT_REACHED;
+                       srq->ibsrq.event_handler(&ev,
+                                                srq->ibsrq.srq_context);
+               } else
+                       spin_unlock(&rq->lock);
+       } else
+               spin_unlock(&rq->lock);
+       ret = 1;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_ruc_loopback - handle UC and RC lookback requests
+ * @sqp: the loopback QP
+ * @wc: the work completion entry
+ *
+ * This is called from ipath_do_uc_send() or ipath_do_rc_send() to
+ * forward a WQE addressed to the same HCA.
+ * Note that although we are single threaded due to the tasklet, we still
+ * have to protect against post_send().  We don't have to worry about
+ * receive interrupts since this is a connected protocol and all packets
+ * will pass through here.
+ */
+void ipath_ruc_loopback(struct ipath_qp *sqp, struct ib_wc *wc)
+{
+       struct ipath_ibdev *dev = to_idev(sqp->ibqp.device);
+       struct ipath_qp *qp;
+       struct ipath_swqe *wqe;
+       struct ipath_sge *sge;
+       unsigned long flags;
+       u64 sdata;
+
+       qp = ipath_lookup_qpn(&dev->qp_table, sqp->remote_qpn);
+       if (!qp) {
+               dev->n_pkt_drops++;
+               return;
+       }
+
+again:
+       spin_lock_irqsave(&sqp->s_lock, flags);
+
+       if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_SEND_OK)) {
+               spin_unlock_irqrestore(&sqp->s_lock, flags);
+               goto done;
+       }
+
+       /* Get the next send request. */
+       if (sqp->s_last == sqp->s_head) {
+               /* Send work queue is empty. */
+               spin_unlock_irqrestore(&sqp->s_lock, flags);
+               goto done;
+       }
+
+       /*
+        * We can rely on the entry not changing without the s_lock
+        * being held until we update s_last.
+        */
+       wqe = get_swqe_ptr(sqp, sqp->s_last);
+       spin_unlock_irqrestore(&sqp->s_lock, flags);
+
+       wc->wc_flags = 0;
+       wc->imm_data = 0;
+
+       sqp->s_sge.sge = wqe->sg_list[0];
+       sqp->s_sge.sg_list = wqe->sg_list + 1;
+       sqp->s_sge.num_sge = wqe->wr.num_sge;
+       sqp->s_len = wqe->length;
+       switch (wqe->wr.opcode) {
+       case IB_WR_SEND_WITH_IMM:
+               wc->wc_flags = IB_WC_WITH_IMM;
+               wc->imm_data = wqe->wr.imm_data;
+               /* FALLTHROUGH */
+       case IB_WR_SEND:
+               spin_lock_irqsave(&qp->r_rq.lock, flags);
+               if (!ipath_get_rwqe(qp, 0)) {
+               rnr_nak:
+                       spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+                       /* Handle RNR NAK */
+                       if (qp->ibqp.qp_type == IB_QPT_UC)
+                               goto send_comp;
+                       if (sqp->s_rnr_retry == 0) {
+                               wc->status = IB_WC_RNR_RETRY_EXC_ERR;
+                               goto err;
+                       }
+                       if (sqp->s_rnr_retry_cnt < 7)
+                               sqp->s_rnr_retry--;
+                       dev->n_rnr_naks++;
+                       sqp->s_rnr_timeout =
+                               ib_ipath_rnr_table[sqp->s_min_rnr_timer];
+                       ipath_insert_rnr_queue(sqp);
+                       goto done;
+               }
+               spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+               break;
+
+       case IB_WR_RDMA_WRITE_WITH_IMM:
+               wc->wc_flags = IB_WC_WITH_IMM;
+               wc->imm_data = wqe->wr.imm_data;
+               spin_lock_irqsave(&qp->r_rq.lock, flags);
+               if (!ipath_get_rwqe(qp, 1))
+                       goto rnr_nak;
+               spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+               /* FALLTHROUGH */
+       case IB_WR_RDMA_WRITE:
+               if (wqe->length == 0)
+                       break;
+               if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, wqe->length,
+                                           wqe->wr.wr.rdma.remote_addr,
+                                           wqe->wr.wr.rdma.rkey,
+                                           IB_ACCESS_REMOTE_WRITE))) {
+               acc_err:
+                       wc->status = IB_WC_REM_ACCESS_ERR;
+               err:
+                       wc->wr_id = wqe->wr.wr_id;
+                       wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
+                       wc->vendor_err = 0;
+                       wc->byte_len = 0;
+                       wc->qp_num = sqp->ibqp.qp_num;
+                       wc->src_qp = sqp->remote_qpn;
+                       wc->pkey_index = 0;
+                       wc->slid = sqp->remote_ah_attr.dlid;
+                       wc->sl = sqp->remote_ah_attr.sl;
+                       wc->dlid_path_bits = 0;
+                       wc->port_num = 0;
+                       ipath_sqerror_qp(sqp, wc);
+                       goto done;
+               }
+               break;
+
+       case IB_WR_RDMA_READ:
+               if (unlikely(!ipath_rkey_ok(dev, &sqp->s_sge, wqe->length,
+                                           wqe->wr.wr.rdma.remote_addr,
+                                           wqe->wr.wr.rdma.rkey,
+                                           IB_ACCESS_REMOTE_READ)))
+                       goto acc_err;
+               if (unlikely(!(qp->qp_access_flags &
+                              IB_ACCESS_REMOTE_READ)))
+                       goto acc_err;
+               qp->r_sge.sge = wqe->sg_list[0];
+               qp->r_sge.sg_list = wqe->sg_list + 1;
+               qp->r_sge.num_sge = wqe->wr.num_sge;
+               break;
+
+       case IB_WR_ATOMIC_CMP_AND_SWP:
+       case IB_WR_ATOMIC_FETCH_AND_ADD:
+               if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, sizeof(u64),
+                                           wqe->wr.wr.rdma.remote_addr,
+                                           wqe->wr.wr.rdma.rkey,
+                                           IB_ACCESS_REMOTE_ATOMIC)))
+                       goto acc_err;
+               /* Perform atomic OP and save result. */
+               sdata = wqe->wr.wr.atomic.swap;
+               spin_lock_irqsave(&dev->pending_lock, flags);
+               qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr;
+               if (wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)
+                       *(u64 *) qp->r_sge.sge.vaddr =
+                               qp->r_atomic_data + sdata;
+               else if (qp->r_atomic_data == wqe->wr.wr.atomic.compare_add)
+                       *(u64 *) qp->r_sge.sge.vaddr = sdata;
+               spin_unlock_irqrestore(&dev->pending_lock, flags);
+               *(u64 *) sqp->s_sge.sge.vaddr = qp->r_atomic_data;
+               goto send_comp;
+
+       default:
+               goto done;
+       }
+
+       sge = &sqp->s_sge.sge;
+       while (sqp->s_len) {
+               u32 len = sqp->s_len;
+
+               if (len > sge->length)
+                       len = sge->length;
+               BUG_ON(len == 0);
+               ipath_copy_sge(&qp->r_sge, sge->vaddr, len);
+               sge->vaddr += len;
+               sge->length -= len;
+               sge->sge_length -= len;
+               if (sge->sge_length == 0) {
+                       if (--sqp->s_sge.num_sge)
+                               *sge = *sqp->s_sge.sg_list++;
+               } else if (sge->length == 0 && sge->mr != NULL) {
+                       if (++sge->n >= IPATH_SEGSZ) {
+                               if (++sge->m >= sge->mr->mapsz)
+                                       break;
+                               sge->n = 0;
+                       }
+                       sge->vaddr =
+                               sge->mr->map[sge->m]->segs[sge->n].vaddr;
+                       sge->length =
+                               sge->mr->map[sge->m]->segs[sge->n].length;
+               }
+               sqp->s_len -= len;
+       }
+
+       if (wqe->wr.opcode == IB_WR_RDMA_WRITE ||
+           wqe->wr.opcode == IB_WR_RDMA_READ)
+               goto send_comp;
+
+       if (wqe->wr.opcode == IB_WR_RDMA_WRITE_WITH_IMM)
+               wc->opcode = IB_WC_RECV_RDMA_WITH_IMM;
+       else
+               wc->opcode = IB_WC_RECV;
+       wc->wr_id = qp->r_wr_id;
+       wc->status = IB_WC_SUCCESS;
+       wc->vendor_err = 0;
+       wc->byte_len = wqe->length;
+       wc->qp_num = qp->ibqp.qp_num;
+       wc->src_qp = qp->remote_qpn;
+       /* XXX do we know which pkey matched? Only needed for GSI. */
+       wc->pkey_index = 0;
+       wc->slid = qp->remote_ah_attr.dlid;
+       wc->sl = qp->remote_ah_attr.sl;
+       wc->dlid_path_bits = 0;
+       /* Signal completion event if the solicited bit is set. */
+       ipath_cq_enter(to_icq(qp->ibqp.recv_cq), wc,
+                      wqe->wr.send_flags & IB_SEND_SOLICITED);
+
+send_comp:
+       sqp->s_rnr_retry = sqp->s_rnr_retry_cnt;
+
+       if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &sqp->s_flags) ||
+           (wqe->wr.send_flags & IB_SEND_SIGNALED)) {
+               wc->wr_id = wqe->wr.wr_id;
+               wc->status = IB_WC_SUCCESS;
+               wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
+               wc->vendor_err = 0;
+               wc->byte_len = wqe->length;
+               wc->qp_num = sqp->ibqp.qp_num;
+               wc->src_qp = 0;
+               wc->pkey_index = 0;
+               wc->slid = 0;
+               wc->sl = 0;
+               wc->dlid_path_bits = 0;
+               wc->port_num = 0;
+               ipath_cq_enter(to_icq(sqp->ibqp.send_cq), wc, 0);
+       }
+
+       /* Update s_last now that we are finished with the SWQE */
+       spin_lock_irqsave(&sqp->s_lock, flags);
+       if (++sqp->s_last >= sqp->s_size)
+               sqp->s_last = 0;
+       spin_unlock_irqrestore(&sqp->s_lock, flags);
+       goto again;
+
+done:
+       if (atomic_dec_and_test(&qp->refcount))
+               wake_up(&qp->wait);
+}
+
+/**
+ * ipath_no_bufs_available - tell the layer driver we need buffers
+ * @qp: the QP that caused the problem
+ * @dev: the device we ran out of buffers on
+ *
+ * Called when we run out of PIO buffers.
+ */
+void ipath_no_bufs_available(struct ipath_qp *qp, struct ipath_ibdev *dev)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->pending_lock, flags);
+       if (qp->piowait.next == LIST_POISON1)
+               list_add_tail(&qp->piowait, &dev->piowait);
+       spin_unlock_irqrestore(&dev->pending_lock, flags);
+       /*
+        * Note that as soon as ipath_layer_want_buffer() is called and
+        * possibly before it returns, ipath_ib_piobufavail()
+        * could be called.  If we are still in the tasklet function,
+        * tasklet_hi_schedule() will not call us until the next time
+        * tasklet_hi_schedule() is called.
+        * We clear the tasklet flag now since we are committing to return
+        * from the tasklet function.
+        */
+       clear_bit(IPATH_S_BUSY, &qp->s_flags);
+       tasklet_unlock(&qp->s_task);
+       ipath_layer_want_buffer(dev->dd);
+       dev->n_piowait++;
+}
+
+/**
+ * ipath_post_rc_send - post RC and UC sends
+ * @qp: the QP to post on
+ * @wr: the work request to send
+ */
+int ipath_post_rc_send(struct ipath_qp *qp, struct ib_send_wr *wr)
+{
+       struct ipath_swqe *wqe;
+       unsigned long flags;
+       u32 next;
+       int i, j;
+       int acc;
+       int ret;
+
+       /*
+        * Don't allow RDMA reads or atomic operations on UC or
+        * undefined operations.
+        * Make sure buffer is large enough to hold the result for atomics.
+        */
+       if (qp->ibqp.qp_type == IB_QPT_UC) {
+               if ((unsigned) wr->opcode >= IB_WR_RDMA_READ) {
+                       ret = -EINVAL;
+                       goto bail;
+               }
+       } else if ((unsigned) wr->opcode > IB_WR_ATOMIC_FETCH_AND_ADD) {
+               ret = -EINVAL;
+               goto bail;
+       } else if (wr->opcode >= IB_WR_ATOMIC_CMP_AND_SWP &&
+                  (wr->num_sge == 0 ||
+                   wr->sg_list[0].length < sizeof(u64) ||
+                   wr->sg_list[0].addr & (sizeof(u64) - 1))) {
+               ret = -EINVAL;
+               goto bail;
+       }
+       /* IB spec says that num_sge == 0 is OK. */
+       if (wr->num_sge > qp->s_max_sge) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+       spin_lock_irqsave(&qp->s_lock, flags);
+       next = qp->s_head + 1;
+       if (next >= qp->s_size)
+               next = 0;
+       if (next == qp->s_last) {
+               spin_unlock_irqrestore(&qp->s_lock, flags);
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       wqe = get_swqe_ptr(qp, qp->s_head);
+       wqe->wr = *wr;
+       wqe->ssn = qp->s_ssn++;
+       wqe->sg_list[0].mr = NULL;
+       wqe->sg_list[0].vaddr = NULL;
+       wqe->sg_list[0].length = 0;
+       wqe->sg_list[0].sge_length = 0;
+       wqe->length = 0;
+       acc = wr->opcode >= IB_WR_RDMA_READ ? IB_ACCESS_LOCAL_WRITE : 0;
+       for (i = 0, j = 0; i < wr->num_sge; i++) {
+               if (to_ipd(qp->ibqp.pd)->user && wr->sg_list[i].lkey == 0) {
+                       spin_unlock_irqrestore(&qp->s_lock, flags);
+                       ret = -EINVAL;
+                       goto bail;
+               }
+               if (wr->sg_list[i].length == 0)
+                       continue;
+               if (!ipath_lkey_ok(&to_idev(qp->ibqp.device)->lk_table,
+                                  &wqe->sg_list[j], &wr->sg_list[i],
+                                  acc)) {
+                       spin_unlock_irqrestore(&qp->s_lock, flags);
+                       ret = -EINVAL;
+                       goto bail;
+               }
+               wqe->length += wr->sg_list[i].length;
+               j++;
+       }
+       wqe->wr.num_sge = j;
+       qp->s_head = next;
+       /*
+        * Wake up the send tasklet if the QP is not waiting
+        * for an RNR timeout.
+        */
+       next = qp->s_rnr_timeout;
+       spin_unlock_irqrestore(&qp->s_lock, flags);
+
+       if (next == 0) {
+               if (qp->ibqp.qp_type == IB_QPT_UC)
+                       ipath_do_uc_send((unsigned long) qp);
+               else
+                       ipath_do_rc_send((unsigned long) qp);
+       }
+
+       ret = 0;
+
+bail:
+       return ret;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_srq.c b/drivers/infiniband/hw/ipath/ipath_srq.c
new file mode 100644 (file)
index 0000000..01c4c6c
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/err.h>
+#include <linux/vmalloc.h>
+
+#include "ipath_verbs.h"
+
+/**
+ * ipath_post_srq_receive - post a receive on a shared receive queue
+ * @ibsrq: the SRQ to post the receive on
+ * @wr: the list of work requests to post
+ * @bad_wr: the first WR to cause a problem is put here
+ *
+ * This may be called from interrupt context.
+ */
+int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr,
+                          struct ib_recv_wr **bad_wr)
+{
+       struct ipath_srq *srq = to_isrq(ibsrq);
+       struct ipath_ibdev *dev = to_idev(ibsrq->device);
+       unsigned long flags;
+       int ret;
+
+       for (; wr; wr = wr->next) {
+               struct ipath_rwqe *wqe;
+               u32 next;
+               int i, j;
+
+               if (wr->num_sge > srq->rq.max_sge) {
+                       *bad_wr = wr;
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+
+               spin_lock_irqsave(&srq->rq.lock, flags);
+               next = srq->rq.head + 1;
+               if (next >= srq->rq.size)
+                       next = 0;
+               if (next == srq->rq.tail) {
+                       spin_unlock_irqrestore(&srq->rq.lock, flags);
+                       *bad_wr = wr;
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+
+               wqe = get_rwqe_ptr(&srq->rq, srq->rq.head);
+               wqe->wr_id = wr->wr_id;
+               wqe->sg_list[0].mr = NULL;
+               wqe->sg_list[0].vaddr = NULL;
+               wqe->sg_list[0].length = 0;
+               wqe->sg_list[0].sge_length = 0;
+               wqe->length = 0;
+               for (i = 0, j = 0; i < wr->num_sge; i++) {
+                       /* Check LKEY */
+                       if (to_ipd(srq->ibsrq.pd)->user &&
+                           wr->sg_list[i].lkey == 0) {
+                               spin_unlock_irqrestore(&srq->rq.lock,
+                                                      flags);
+                               *bad_wr = wr;
+                               ret = -EINVAL;
+                               goto bail;
+                       }
+                       if (wr->sg_list[i].length == 0)
+                               continue;
+                       if (!ipath_lkey_ok(&dev->lk_table,
+                                          &wqe->sg_list[j],
+                                          &wr->sg_list[i],
+                                          IB_ACCESS_LOCAL_WRITE)) {
+                               spin_unlock_irqrestore(&srq->rq.lock,
+                                                      flags);
+                               *bad_wr = wr;
+                               ret = -EINVAL;
+                               goto bail;
+                       }
+                       wqe->length += wr->sg_list[i].length;
+                       j++;
+               }
+               wqe->num_sge = j;
+               srq->rq.head = next;
+               spin_unlock_irqrestore(&srq->rq.lock, flags);
+       }
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_create_srq - create a shared receive queue
+ * @ibpd: the protection domain of the SRQ to create
+ * @attr: the attributes of the SRQ
+ * @udata: not used by the InfiniPath verbs driver
+ */
+struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
+                               struct ib_srq_init_attr *srq_init_attr,
+                               struct ib_udata *udata)
+{
+       struct ipath_srq *srq;
+       u32 sz;
+       struct ib_srq *ret;
+
+       if (srq_init_attr->attr.max_sge < 1) {
+               ret = ERR_PTR(-EINVAL);
+               goto bail;
+       }
+
+       srq = kmalloc(sizeof(*srq), GFP_KERNEL);
+       if (!srq) {
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       /*
+        * Need to use vmalloc() if we want to support large #s of entries.
+        */
+       srq->rq.size = srq_init_attr->attr.max_wr + 1;
+       sz = sizeof(struct ipath_sge) * srq_init_attr->attr.max_sge +
+               sizeof(struct ipath_rwqe);
+       srq->rq.wq = vmalloc(srq->rq.size * sz);
+       if (!srq->rq.wq) {
+               kfree(srq);
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       /*
+        * ib_create_srq() will initialize srq->ibsrq.
+        */
+       spin_lock_init(&srq->rq.lock);
+       srq->rq.head = 0;
+       srq->rq.tail = 0;
+       srq->rq.max_sge = srq_init_attr->attr.max_sge;
+       srq->limit = srq_init_attr->attr.srq_limit;
+
+       ret = &srq->ibsrq;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_modify_srq - modify a shared receive queue
+ * @ibsrq: the SRQ to modify
+ * @attr: the new attributes of the SRQ
+ * @attr_mask: indicates which attributes to modify
+ */
+int ipath_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
+                    enum ib_srq_attr_mask attr_mask)
+{
+       struct ipath_srq *srq = to_isrq(ibsrq);
+       unsigned long flags;
+       int ret;
+
+       if (attr_mask & IB_SRQ_LIMIT) {
+               spin_lock_irqsave(&srq->rq.lock, flags);
+               srq->limit = attr->srq_limit;
+               spin_unlock_irqrestore(&srq->rq.lock, flags);
+       }
+       if (attr_mask & IB_SRQ_MAX_WR) {
+               u32 size = attr->max_wr + 1;
+               struct ipath_rwqe *wq, *p;
+               u32 n;
+               u32 sz;
+
+               if (attr->max_sge < srq->rq.max_sge) {
+                       ret = -EINVAL;
+                       goto bail;
+               }
+
+               sz = sizeof(struct ipath_rwqe) +
+                       attr->max_sge * sizeof(struct ipath_sge);
+               wq = vmalloc(size * sz);
+               if (!wq) {
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+
+               spin_lock_irqsave(&srq->rq.lock, flags);
+               if (srq->rq.head < srq->rq.tail)
+                       n = srq->rq.size + srq->rq.head - srq->rq.tail;
+               else
+                       n = srq->rq.head - srq->rq.tail;
+               if (size <= n || size <= srq->limit) {
+                       spin_unlock_irqrestore(&srq->rq.lock, flags);
+                       vfree(wq);
+                       ret = -EINVAL;
+                       goto bail;
+               }
+               n = 0;
+               p = wq;
+               while (srq->rq.tail != srq->rq.head) {
+                       struct ipath_rwqe *wqe;
+                       int i;
+
+                       wqe = get_rwqe_ptr(&srq->rq, srq->rq.tail);
+                       p->wr_id = wqe->wr_id;
+                       p->length = wqe->length;
+                       p->num_sge = wqe->num_sge;
+                       for (i = 0; i < wqe->num_sge; i++)
+                               p->sg_list[i] = wqe->sg_list[i];
+                       n++;
+                       p = (struct ipath_rwqe *)((char *) p + sz);
+                       if (++srq->rq.tail >= srq->rq.size)
+                               srq->rq.tail = 0;
+               }
+               vfree(srq->rq.wq);
+               srq->rq.wq = wq;
+               srq->rq.size = size;
+               srq->rq.head = n;
+               srq->rq.tail = 0;
+               srq->rq.max_sge = attr->max_sge;
+               spin_unlock_irqrestore(&srq->rq.lock, flags);
+       }
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+int ipath_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr)
+{
+       struct ipath_srq *srq = to_isrq(ibsrq);
+
+       attr->max_wr = srq->rq.size - 1;
+       attr->max_sge = srq->rq.max_sge;
+       attr->srq_limit = srq->limit;
+       return 0;
+}
+
+/**
+ * ipath_destroy_srq - destroy a shared receive queue
+ * @ibsrq: the SRQ to destroy
+ */
+int ipath_destroy_srq(struct ib_srq *ibsrq)
+{
+       struct ipath_srq *srq = to_isrq(ibsrq);
+
+       vfree(srq->rq.wq);
+       kfree(srq);
+
+       return 0;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_stats.c b/drivers/infiniband/hw/ipath/ipath_stats.c
new file mode 100644 (file)
index 0000000..fe20913
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/pci.h>
+
+#include "ipath_kernel.h"
+
+struct infinipath_stats ipath_stats;
+
+/**
+ * ipath_snap_cntr - snapshot a chip counter
+ * @dd: the infinipath device
+ * @creg: the counter to snapshot
+ *
+ * called from add_timer and user counter read calls, to deal with
+ * counters that wrap in "human time".  The words sent and received, and
+ * the packets sent and received are all that we worry about.  For now,
+ * at least, we don't worry about error counters, because if they wrap
+ * that quickly, we probably don't care.  We may eventually just make this
+ * handle all the counters.  word counters can wrap in about 20 seconds
+ * of full bandwidth traffic, packet counters in a few hours.
+ */
+
+u64 ipath_snap_cntr(struct ipath_devdata *dd, ipath_creg creg)
+{
+       u32 val, reg64 = 0;
+       u64 val64;
+       unsigned long t0, t1;
+       u64 ret;
+
+       t0 = jiffies;
+       /* If fast increment counters are only 32 bits, snapshot them,
+        * and maintain them as 64bit values in the driver */
+       if (!(dd->ipath_flags & IPATH_32BITCOUNTERS) &&
+           (creg == dd->ipath_cregs->cr_wordsendcnt ||
+            creg == dd->ipath_cregs->cr_wordrcvcnt ||
+            creg == dd->ipath_cregs->cr_pktsendcnt ||
+            creg == dd->ipath_cregs->cr_pktrcvcnt)) {
+               val64 = ipath_read_creg(dd, creg);
+               val = val64 == ~0ULL ? ~0U : 0;
+               reg64 = 1;
+       } else                  /* val64 just to keep gcc quiet... */
+               val64 = val = ipath_read_creg32(dd, creg);
+       /*
+        * See if a second has passed.  This is just a way to detect things
+        * that are quite broken.  Normally this should take just a few
+        * cycles (the check is for long enough that we don't care if we get
+        * pre-empted.)  An Opteron HT O read timeout is 4 seconds with
+        * normal NB values
+        */
+       t1 = jiffies;
+       if (time_before(t0 + HZ, t1) && val == -1) {
+               ipath_dev_err(dd, "Error!  Read counter 0x%x timed out\n",
+                             creg);
+               ret = 0ULL;
+               goto bail;
+       }
+       if (reg64) {
+               ret = val64;
+               goto bail;
+       }
+
+       if (creg == dd->ipath_cregs->cr_wordsendcnt) {
+               if (val != dd->ipath_lastsword) {
+                       dd->ipath_sword += val - dd->ipath_lastsword;
+                       dd->ipath_lastsword = val;
+               }
+               val64 = dd->ipath_sword;
+       } else if (creg == dd->ipath_cregs->cr_wordrcvcnt) {
+               if (val != dd->ipath_lastrword) {
+                       dd->ipath_rword += val - dd->ipath_lastrword;
+                       dd->ipath_lastrword = val;
+               }
+               val64 = dd->ipath_rword;
+       } else if (creg == dd->ipath_cregs->cr_pktsendcnt) {
+               if (val != dd->ipath_lastspkts) {
+                       dd->ipath_spkts += val - dd->ipath_lastspkts;
+                       dd->ipath_lastspkts = val;
+               }
+               val64 = dd->ipath_spkts;
+       } else if (creg == dd->ipath_cregs->cr_pktrcvcnt) {
+               if (val != dd->ipath_lastrpkts) {
+                       dd->ipath_rpkts += val - dd->ipath_lastrpkts;
+                       dd->ipath_lastrpkts = val;
+               }
+               val64 = dd->ipath_rpkts;
+       } else
+               val64 = (u64) val;
+
+       ret = val64;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_qcheck - print delta of egrfull/hdrqfull errors for kernel ports
+ * @dd: the infinipath device
+ *
+ * print the delta of egrfull/hdrqfull errors for kernel ports no more than
+ * every 5 seconds.  User processes are printed at close, but kernel doesn't
+ * close, so...  Separate routine so may call from other places someday, and
+ * so function name when printed by _IPATH_INFO is meaningfull
+ */
+static void ipath_qcheck(struct ipath_devdata *dd)
+{
+       static u64 last_tot_hdrqfull;
+       size_t blen = 0;
+       char buf[128];
+
+       *buf = 0;
+       if (dd->ipath_pd[0]->port_hdrqfull != dd->ipath_p0_hdrqfull) {
+               blen = snprintf(buf, sizeof buf, "port 0 hdrqfull %u",
+                               dd->ipath_pd[0]->port_hdrqfull -
+                               dd->ipath_p0_hdrqfull);
+               dd->ipath_p0_hdrqfull = dd->ipath_pd[0]->port_hdrqfull;
+       }
+       if (ipath_stats.sps_etidfull != dd->ipath_last_tidfull) {
+               blen += snprintf(buf + blen, sizeof buf - blen,
+                                "%srcvegrfull %llu",
+                                blen ? ", " : "",
+                                (unsigned long long)
+                                (ipath_stats.sps_etidfull -
+                                 dd->ipath_last_tidfull));
+               dd->ipath_last_tidfull = ipath_stats.sps_etidfull;
+       }
+
+       /*
+        * this is actually the number of hdrq full interrupts, not actual
+        * events, but at the moment that's mostly what I'm interested in.
+        * Actual count, etc. is in the counters, if needed.  For production
+        * users this won't ordinarily be printed.
+        */
+
+       if ((ipath_debug & (__IPATH_PKTDBG | __IPATH_DBG)) &&
+           ipath_stats.sps_hdrqfull != last_tot_hdrqfull) {
+               blen += snprintf(buf + blen, sizeof buf - blen,
+                                "%shdrqfull %llu (all ports)",
+                                blen ? ", " : "",
+                                (unsigned long long)
+                                (ipath_stats.sps_hdrqfull -
+                                 last_tot_hdrqfull));
+               last_tot_hdrqfull = ipath_stats.sps_hdrqfull;
+       }
+       if (blen)
+               ipath_dbg("%s\n", buf);
+
+       if (dd->ipath_port0head != (u32)
+           le64_to_cpu(*dd->ipath_hdrqtailptr)) {
+               if (dd->ipath_lastport0rcv_cnt ==
+                   ipath_stats.sps_port0pkts) {
+                       ipath_cdbg(PKT, "missing rcv interrupts? "
+                                  "port0 hd=%llx tl=%x; port0pkts %llx\n",
+                                  (unsigned long long)
+                                  le64_to_cpu(*dd->ipath_hdrqtailptr),
+                                  dd->ipath_port0head,
+                                  (unsigned long long)
+                                  ipath_stats.sps_port0pkts);
+                       ipath_kreceive(dd);
+               }
+               dd->ipath_lastport0rcv_cnt = ipath_stats.sps_port0pkts;
+       }
+}
+
+/**
+ * ipath_get_faststats - get word counters from chip before they overflow
+ * @opaque - contains a pointer to the infinipath device ipath_devdata
+ *
+ * called from add_timer
+ */
+void ipath_get_faststats(unsigned long opaque)
+{
+       struct ipath_devdata *dd = (struct ipath_devdata *) opaque;
+       u32 val;
+       static unsigned cnt;
+
+       /*
+        * don't access the chip while running diags, or memory diags can
+        * fail
+        */
+       if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT) ||
+           ipath_diag_inuse)
+               /* but re-arm the timer, for diags case; won't hurt other */
+               goto done;
+
+       if (dd->ipath_flags & IPATH_32BITCOUNTERS) {
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordsendcnt);
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordrcvcnt);
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktsendcnt);
+               ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktrcvcnt);
+       }
+
+       ipath_qcheck(dd);
+
+       /*
+        * deal with repeat error suppression.  Doesn't really matter if
+        * last error was almost a full interval ago, or just a few usecs
+        * ago; still won't get more than 2 per interval.  We may want
+        * longer intervals for this eventually, could do with mod, counter
+        * or separate timer.  Also see code in ipath_handle_errors() and
+        * ipath_handle_hwerrors().
+        */
+
+       if (dd->ipath_lasterror)
+               dd->ipath_lasterror = 0;
+       if (dd->ipath_lasthwerror)
+               dd->ipath_lasthwerror = 0;
+       if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs)
+           && time_after(jiffies, dd->ipath_unmasktime)) {
+               char ebuf[256];
+               ipath_decode_err(ebuf, sizeof ebuf,
+                                (dd->ipath_maskederrs & ~dd->
+                                 ipath_ignorederrs));
+               if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) &
+                   ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL))
+                       ipath_dev_err(dd, "Re-enabling masked errors "
+                                     "(%s)\n", ebuf);
+               else {
+                       /*
+                        * rcvegrfull and rcvhdrqfull are "normal", for some
+                        * types of processes (mostly benchmarks) that send
+                        * huge numbers of messages, while not processing
+                        * them.  So only complain about these at debug
+                        * level.
+                        */
+                       ipath_dbg("Disabling frequent queue full errors "
+                                 "(%s)\n", ebuf);
+               }
+               dd->ipath_maskederrs = dd->ipath_ignorederrs;
+               ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
+                                ~dd->ipath_maskederrs);
+       }
+
+       /* limit qfull messages to ~one per minute per port */
+       if ((++cnt & 0x10)) {
+               for (val = dd->ipath_cfgports - 1; ((int)val) >= 0;
+                    val--) {
+                       if (dd->ipath_lastegrheads[val] != -1)
+                               dd->ipath_lastegrheads[val] = -1;
+                       if (dd->ipath_lastrcvhdrqtails[val] != -1)
+                               dd->ipath_lastrcvhdrqtails[val] = -1;
+               }
+       }
+
+       if (dd->ipath_nosma_bufs) {
+               dd->ipath_nosma_secs += 5;
+               if (dd->ipath_nosma_secs >= 30) {
+                       ipath_cdbg(SMA, "No SMA bufs avail %u seconds; "
+                                  "cancelling pending sends\n",
+                                  dd->ipath_nosma_secs);
+                       /*
+                        * issue an abort as well, in case we have a packet
+                        * stuck in launch fifo.  This could corrupt an
+                        * outgoing user packet in the worst case,
+                        * but this is a pretty catastrophic, anyway.
+                        */
+                       ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+                                        INFINIPATH_S_ABORT);
+                       ipath_disarm_piobufs(dd, dd->ipath_lastport_piobuf,
+                                            dd->ipath_piobcnt2k +
+                                            dd->ipath_piobcnt4k -
+                                            dd->ipath_lastport_piobuf);
+                       /* start again, if necessary */
+                       dd->ipath_nosma_secs = 0;
+               } else
+                       ipath_cdbg(SMA, "No SMA bufs avail %u tries, "
+                                  "after %u seconds\n",
+                                  dd->ipath_nosma_bufs,
+                                  dd->ipath_nosma_secs);
+       }
+
+done:
+       mod_timer(&dd->ipath_stats_timer, jiffies + HZ * 5);
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_sysfs.c b/drivers/infiniband/hw/ipath/ipath_sysfs.c
new file mode 100644 (file)
index 0000000..32acd80
--- /dev/null
@@ -0,0 +1,778 @@
+/*
+ * Copyright (c) 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/ctype.h>
+#include <linux/pci.h>
+
+#include "ipath_kernel.h"
+#include "ips_common.h"
+#include "ipath_layer.h"
+
+/**
+ * ipath_parse_ushort - parse an unsigned short value in an arbitrary base
+ * @str: the string containing the number
+ * @valp: where to put the result
+ *
+ * returns the number of bytes consumed, or negative value on error
+ */
+int ipath_parse_ushort(const char *str, unsigned short *valp)
+{
+       unsigned long val;
+       char *end;
+       int ret;
+
+       if (!isdigit(str[0])) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       val = simple_strtoul(str, &end, 0);
+
+       if (val > 0xffff) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       *valp = val;
+
+       ret = end + 1 - str;
+       if (ret == 0)
+               ret = -EINVAL;
+
+bail:
+       return ret;
+}
+
+static ssize_t show_version(struct device_driver *dev, char *buf)
+{
+       /* The string printed here is already newline-terminated. */
+       return scnprintf(buf, PAGE_SIZE, "%s", ipath_core_version);
+}
+
+static ssize_t show_num_units(struct device_driver *dev, char *buf)
+{
+       return scnprintf(buf, PAGE_SIZE, "%d\n",
+                        ipath_count_units(NULL, NULL, NULL));
+}
+
+#define DRIVER_STAT(name, attr) \
+       static ssize_t show_stat_##name(struct device_driver *dev, \
+                                       char *buf) \
+       { \
+               return scnprintf( \
+                       buf, PAGE_SIZE, "%llu\n", \
+                       (unsigned long long) ipath_stats.sps_ ##attr); \
+       } \
+       static DRIVER_ATTR(name, S_IRUGO, show_stat_##name, NULL)
+
+DRIVER_STAT(intrs, ints);
+DRIVER_STAT(err_intrs, errints);
+DRIVER_STAT(errs, errs);
+DRIVER_STAT(pkt_errs, pkterrs);
+DRIVER_STAT(crc_errs, crcerrs);
+DRIVER_STAT(hw_errs, hwerrs);
+DRIVER_STAT(ib_link, iblink);
+DRIVER_STAT(port0_pkts, port0pkts);
+DRIVER_STAT(ether_spkts, ether_spkts);
+DRIVER_STAT(ether_rpkts, ether_rpkts);
+DRIVER_STAT(sma_spkts, sma_spkts);
+DRIVER_STAT(sma_rpkts, sma_rpkts);
+DRIVER_STAT(hdrq_full, hdrqfull);
+DRIVER_STAT(etid_full, etidfull);
+DRIVER_STAT(no_piobufs, nopiobufs);
+DRIVER_STAT(ports, ports);
+DRIVER_STAT(pkey0, pkeys[0]);
+DRIVER_STAT(pkey1, pkeys[1]);
+DRIVER_STAT(pkey2, pkeys[2]);
+DRIVER_STAT(pkey3, pkeys[3]);
+/* XXX fix the following when dynamic table of devices used */
+DRIVER_STAT(lid0, lid[0]);
+DRIVER_STAT(lid1, lid[1]);
+DRIVER_STAT(lid2, lid[2]);
+DRIVER_STAT(lid3, lid[3]);
+
+DRIVER_STAT(nports, nports);
+DRIVER_STAT(null_intr, nullintr);
+DRIVER_STAT(max_pkts_call, maxpkts_call);
+DRIVER_STAT(avg_pkts_call, avgpkts_call);
+DRIVER_STAT(page_locks, pagelocks);
+DRIVER_STAT(page_unlocks, pageunlocks);
+DRIVER_STAT(krdrops, krdrops);
+/* XXX fix the following when dynamic table of devices used */
+DRIVER_STAT(mlid0, mlid[0]);
+DRIVER_STAT(mlid1, mlid[1]);
+DRIVER_STAT(mlid2, mlid[2]);
+DRIVER_STAT(mlid3, mlid[3]);
+
+static struct attribute *driver_stat_attributes[] = {
+       &driver_attr_intrs.attr,
+       &driver_attr_err_intrs.attr,
+       &driver_attr_errs.attr,
+       &driver_attr_pkt_errs.attr,
+       &driver_attr_crc_errs.attr,
+       &driver_attr_hw_errs.attr,
+       &driver_attr_ib_link.attr,
+       &driver_attr_port0_pkts.attr,
+       &driver_attr_ether_spkts.attr,
+       &driver_attr_ether_rpkts.attr,
+       &driver_attr_sma_spkts.attr,
+       &driver_attr_sma_rpkts.attr,
+       &driver_attr_hdrq_full.attr,
+       &driver_attr_etid_full.attr,
+       &driver_attr_no_piobufs.attr,
+       &driver_attr_ports.attr,
+       &driver_attr_pkey0.attr,
+       &driver_attr_pkey1.attr,
+       &driver_attr_pkey2.attr,
+       &driver_attr_pkey3.attr,
+       &driver_attr_lid0.attr,
+       &driver_attr_lid1.attr,
+       &driver_attr_lid2.attr,
+       &driver_attr_lid3.attr,
+       &driver_attr_nports.attr,
+       &driver_attr_null_intr.attr,
+       &driver_attr_max_pkts_call.attr,
+       &driver_attr_avg_pkts_call.attr,
+       &driver_attr_page_locks.attr,
+       &driver_attr_page_unlocks.attr,
+       &driver_attr_krdrops.attr,
+       &driver_attr_mlid0.attr,
+       &driver_attr_mlid1.attr,
+       &driver_attr_mlid2.attr,
+       &driver_attr_mlid3.attr,
+       NULL
+};
+
+static struct attribute_group driver_stat_attr_group = {
+       .name = "stats",
+       .attrs = driver_stat_attributes
+};
+
+static ssize_t show_status(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       ssize_t ret;
+
+       if (!dd->ipath_statusp) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       ret = scnprintf(buf, PAGE_SIZE, "0x%llx\n",
+                       (unsigned long long) *(dd->ipath_statusp));
+
+bail:
+       return ret;
+}
+
+static const char *ipath_status_str[] = {
+       "Initted",
+       "Disabled",
+       "Admin_Disabled",
+       "OIB_SMA",
+       "SMA",
+       "Present",
+       "IB_link_up",
+       "IB_configured",
+       "NoIBcable",
+       "Fatal_Hardware_Error",
+       NULL,
+};
+
+static ssize_t show_status_str(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       int i, any;
+       u64 s;
+       ssize_t ret;
+
+       if (!dd->ipath_statusp) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       s = *(dd->ipath_statusp);
+       *buf = '\0';
+       for (any = i = 0; s && ipath_status_str[i]; i++) {
+               if (s & 1) {
+                       if (any && strlcat(buf, " ", PAGE_SIZE) >=
+                           PAGE_SIZE)
+                               /* overflow */
+                               break;
+                       if (strlcat(buf, ipath_status_str[i],
+                                   PAGE_SIZE) >= PAGE_SIZE)
+                               break;
+                       any = 1;
+               }
+               s >>= 1;
+       }
+       if (any)
+               strlcat(buf, "\n", PAGE_SIZE);
+
+       ret = strlen(buf);
+
+bail:
+       return ret;
+}
+
+static ssize_t show_boardversion(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       /* The string printed here is already newline-terminated. */
+       return scnprintf(buf, PAGE_SIZE, "%s", dd->ipath_boardversion);
+}
+
+static ssize_t show_lid(struct device *dev,
+                       struct device_attribute *attr,
+                       char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+
+       return scnprintf(buf, PAGE_SIZE, "0x%x\n", dd->ipath_lid);
+}
+
+static ssize_t store_lid(struct device *dev,
+                        struct device_attribute *attr,
+                         const char *buf,
+                         size_t count)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       u16 lid;
+       int ret;
+
+       ret = ipath_parse_ushort(buf, &lid);
+       if (ret < 0)
+               goto invalid;
+
+       if (lid == 0 || lid >= 0xc000) {
+               ret = -EINVAL;
+               goto invalid;
+       }
+
+       ipath_set_sps_lid(dd, lid, 0);
+
+       goto bail;
+invalid:
+       ipath_dev_err(dd, "attempt to set invalid LID\n");
+bail:
+       return ret;
+}
+
+static ssize_t show_mlid(struct device *dev,
+                        struct device_attribute *attr,
+                        char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+
+       return scnprintf(buf, PAGE_SIZE, "0x%x\n", dd->ipath_mlid);
+}
+
+static ssize_t store_mlid(struct device *dev,
+                        struct device_attribute *attr,
+                         const char *buf,
+                         size_t count)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       int unit;
+       u16 mlid;
+       int ret;
+
+       ret = ipath_parse_ushort(buf, &mlid);
+       if (ret < 0)
+               goto invalid;
+
+       unit = dd->ipath_unit;
+
+       dd->ipath_mlid = mlid;
+       ipath_stats.sps_mlid[unit] = mlid;
+       ipath_layer_intr(dd, IPATH_LAYER_INT_BCAST);
+
+       goto bail;
+invalid:
+       ipath_dev_err(dd, "attempt to set invalid MLID\n");
+bail:
+       return ret;
+}
+
+static ssize_t show_guid(struct device *dev,
+                        struct device_attribute *attr,
+                        char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       u8 *guid;
+
+       guid = (u8 *) & (dd->ipath_guid);
+
+       return scnprintf(buf, PAGE_SIZE,
+                        "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+                        guid[0], guid[1], guid[2], guid[3],
+                        guid[4], guid[5], guid[6], guid[7]);
+}
+
+static ssize_t store_guid(struct device *dev,
+                        struct device_attribute *attr,
+                         const char *buf,
+                         size_t count)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       ssize_t ret;
+       unsigned short guid[8];
+       __be64 nguid;
+       u8 *ng;
+       int i;
+
+       if (sscanf(buf, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
+                  &guid[0], &guid[1], &guid[2], &guid[3],
+                  &guid[4], &guid[5], &guid[6], &guid[7]) != 8)
+               goto invalid;
+
+       ng = (u8 *) &nguid;
+
+       for (i = 0; i < 8; i++) {
+               if (guid[i] > 0xff)
+                       goto invalid;
+               ng[i] = guid[i];
+       }
+
+       dd->ipath_guid = nguid;
+       dd->ipath_nguid = 1;
+
+       ret = strlen(buf);
+       goto bail;
+
+invalid:
+       ipath_dev_err(dd, "attempt to set invalid GUID\n");
+       ret = -EINVAL;
+
+bail:
+       return ret;
+}
+
+static ssize_t show_nguid(struct device *dev,
+                         struct device_attribute *attr,
+                         char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_nguid);
+}
+
+static ssize_t show_serial(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+
+       buf[sizeof dd->ipath_serial] = '\0';
+       memcpy(buf, dd->ipath_serial, sizeof dd->ipath_serial);
+       strcat(buf, "\n");
+       return strlen(buf);
+}
+
+static ssize_t show_unit(struct device *dev,
+                        struct device_attribute *attr,
+                        char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_unit);
+}
+
+#define DEVICE_COUNTER(name, attr) \
+       static ssize_t show_counter_##name(struct device *dev, \
+                                          struct device_attribute *attr, \
+                                          char *buf) \
+       { \
+               struct ipath_devdata *dd = dev_get_drvdata(dev); \
+               return scnprintf(\
+                       buf, PAGE_SIZE, "%llu\n", (unsigned long long) \
+                       ipath_snap_cntr( \
+                               dd, offsetof(struct infinipath_counters, \
+                                            attr) / sizeof(u64)));     \
+       } \
+       static DEVICE_ATTR(name, S_IRUGO, show_counter_##name, NULL);
+
+DEVICE_COUNTER(ib_link_downeds, IBLinkDownedCnt);
+DEVICE_COUNTER(ib_link_err_recoveries, IBLinkErrRecoveryCnt);
+DEVICE_COUNTER(ib_status_changes, IBStatusChangeCnt);
+DEVICE_COUNTER(ib_symbol_errs, IBSymbolErrCnt);
+DEVICE_COUNTER(lb_flow_stalls, LBFlowStallCnt);
+DEVICE_COUNTER(lb_ints, LBIntCnt);
+DEVICE_COUNTER(rx_bad_formats, RxBadFormatCnt);
+DEVICE_COUNTER(rx_buf_ovfls, RxBufOvflCnt);
+DEVICE_COUNTER(rx_data_pkts, RxDataPktCnt);
+DEVICE_COUNTER(rx_dropped_pkts, RxDroppedPktCnt);
+DEVICE_COUNTER(rx_dwords, RxDwordCnt);
+DEVICE_COUNTER(rx_ebps, RxEBPCnt);
+DEVICE_COUNTER(rx_flow_ctrl_errs, RxFlowCtrlErrCnt);
+DEVICE_COUNTER(rx_flow_pkts, RxFlowPktCnt);
+DEVICE_COUNTER(rx_icrc_errs, RxICRCErrCnt);
+DEVICE_COUNTER(rx_len_errs, RxLenErrCnt);
+DEVICE_COUNTER(rx_link_problems, RxLinkProblemCnt);
+DEVICE_COUNTER(rx_lpcrc_errs, RxLPCRCErrCnt);
+DEVICE_COUNTER(rx_max_min_len_errs, RxMaxMinLenErrCnt);
+DEVICE_COUNTER(rx_p0_hdr_egr_ovfls, RxP0HdrEgrOvflCnt);
+DEVICE_COUNTER(rx_p1_hdr_egr_ovfls, RxP1HdrEgrOvflCnt);
+DEVICE_COUNTER(rx_p2_hdr_egr_ovfls, RxP2HdrEgrOvflCnt);
+DEVICE_COUNTER(rx_p3_hdr_egr_ovfls, RxP3HdrEgrOvflCnt);
+DEVICE_COUNTER(rx_p4_hdr_egr_ovfls, RxP4HdrEgrOvflCnt);
+DEVICE_COUNTER(rx_p5_hdr_egr_ovfls, RxP5HdrEgrOvflCnt);
+DEVICE_COUNTER(rx_p6_hdr_egr_ovfls, RxP6HdrEgrOvflCnt);
+DEVICE_COUNTER(rx_p7_hdr_egr_ovfls, RxP7HdrEgrOvflCnt);
+DEVICE_COUNTER(rx_p8_hdr_egr_ovfls, RxP8HdrEgrOvflCnt);
+DEVICE_COUNTER(rx_pkey_mismatches, RxPKeyMismatchCnt);
+DEVICE_COUNTER(rx_tid_full_errs, RxTIDFullErrCnt);
+DEVICE_COUNTER(rx_tid_valid_errs, RxTIDValidErrCnt);
+DEVICE_COUNTER(rx_vcrc_errs, RxVCRCErrCnt);
+DEVICE_COUNTER(tx_data_pkts, TxDataPktCnt);
+DEVICE_COUNTER(tx_dropped_pkts, TxDroppedPktCnt);
+DEVICE_COUNTER(tx_dwords, TxDwordCnt);
+DEVICE_COUNTER(tx_flow_pkts, TxFlowPktCnt);
+DEVICE_COUNTER(tx_flow_stalls, TxFlowStallCnt);
+DEVICE_COUNTER(tx_len_errs, TxLenErrCnt);
+DEVICE_COUNTER(tx_max_min_len_errs, TxMaxMinLenErrCnt);
+DEVICE_COUNTER(tx_underruns, TxUnderrunCnt);
+DEVICE_COUNTER(tx_unsup_vl_errs, TxUnsupVLErrCnt);
+
+static struct attribute *dev_counter_attributes[] = {
+       &dev_attr_ib_link_downeds.attr,
+       &dev_attr_ib_link_err_recoveries.attr,
+       &dev_attr_ib_status_changes.attr,
+       &dev_attr_ib_symbol_errs.attr,
+       &dev_attr_lb_flow_stalls.attr,
+       &dev_attr_lb_ints.attr,
+       &dev_attr_rx_bad_formats.attr,
+       &dev_attr_rx_buf_ovfls.attr,
+       &dev_attr_rx_data_pkts.attr,
+       &dev_attr_rx_dropped_pkts.attr,
+       &dev_attr_rx_dwords.attr,
+       &dev_attr_rx_ebps.attr,
+       &dev_attr_rx_flow_ctrl_errs.attr,
+       &dev_attr_rx_flow_pkts.attr,
+       &dev_attr_rx_icrc_errs.attr,
+       &dev_attr_rx_len_errs.attr,
+       &dev_attr_rx_link_problems.attr,
+       &dev_attr_rx_lpcrc_errs.attr,
+       &dev_attr_rx_max_min_len_errs.attr,
+       &dev_attr_rx_p0_hdr_egr_ovfls.attr,
+       &dev_attr_rx_p1_hdr_egr_ovfls.attr,
+       &dev_attr_rx_p2_hdr_egr_ovfls.attr,
+       &dev_attr_rx_p3_hdr_egr_ovfls.attr,
+       &dev_attr_rx_p4_hdr_egr_ovfls.attr,
+       &dev_attr_rx_p5_hdr_egr_ovfls.attr,
+       &dev_attr_rx_p6_hdr_egr_ovfls.attr,
+       &dev_attr_rx_p7_hdr_egr_ovfls.attr,
+       &dev_attr_rx_p8_hdr_egr_ovfls.attr,
+       &dev_attr_rx_pkey_mismatches.attr,
+       &dev_attr_rx_tid_full_errs.attr,
+       &dev_attr_rx_tid_valid_errs.attr,
+       &dev_attr_rx_vcrc_errs.attr,
+       &dev_attr_tx_data_pkts.attr,
+       &dev_attr_tx_dropped_pkts.attr,
+       &dev_attr_tx_dwords.attr,
+       &dev_attr_tx_flow_pkts.attr,
+       &dev_attr_tx_flow_stalls.attr,
+       &dev_attr_tx_len_errs.attr,
+       &dev_attr_tx_max_min_len_errs.attr,
+       &dev_attr_tx_underruns.attr,
+       &dev_attr_tx_unsup_vl_errs.attr,
+       NULL
+};
+
+static struct attribute_group dev_counter_attr_group = {
+       .name = "counters",
+       .attrs = dev_counter_attributes
+};
+
+static ssize_t store_reset(struct device *dev,
+                        struct device_attribute *attr,
+                         const char *buf,
+                         size_t count)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       int ret;
+
+       if (count < 5 || memcmp(buf, "reset", 5)) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       if (dd->ipath_flags & IPATH_DISABLED) {
+               /*
+                * post-reset init would re-enable interrupts, etc.
+                * so don't allow reset on disabled devices.  Not
+                * perfect error, but about the best choice.
+                */
+               dev_info(dev,"Unit %d is disabled, can't reset\n",
+                        dd->ipath_unit);
+               ret = -EINVAL;
+       }
+       ret = ipath_reset_device(dd->ipath_unit);
+bail:
+       return ret<0 ? ret : count;
+}
+
+static ssize_t store_link_state(struct device *dev,
+                        struct device_attribute *attr,
+                         const char *buf,
+                         size_t count)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       int ret, r;
+       u16 state;
+
+       ret = ipath_parse_ushort(buf, &state);
+       if (ret < 0)
+               goto invalid;
+
+       r = ipath_layer_set_linkstate(dd, state);
+       if (r < 0) {
+               ret = r;
+               goto bail;
+       }
+
+       goto bail;
+invalid:
+       ipath_dev_err(dd, "attempt to set invalid link state\n");
+bail:
+       return ret;
+}
+
+static ssize_t show_mtu(struct device *dev,
+                        struct device_attribute *attr,
+                        char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_ibmtu);
+}
+
+static ssize_t store_mtu(struct device *dev,
+                        struct device_attribute *attr,
+                         const char *buf,
+                         size_t count)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       ssize_t ret;
+       u16 mtu = 0;
+       int r;
+
+       ret = ipath_parse_ushort(buf, &mtu);
+       if (ret < 0)
+               goto invalid;
+
+       r = ipath_layer_set_mtu(dd, mtu);
+       if (r < 0)
+               ret = r;
+
+       goto bail;
+invalid:
+       ipath_dev_err(dd, "attempt to set invalid MTU\n");
+bail:
+       return ret;
+}
+
+static ssize_t show_enabled(struct device *dev,
+                        struct device_attribute *attr,
+                        char *buf)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       return scnprintf(buf, PAGE_SIZE, "%u\n",
+                        (dd->ipath_flags & IPATH_DISABLED) ? 0 : 1);
+}
+
+static ssize_t store_enabled(struct device *dev,
+                        struct device_attribute *attr,
+                         const char *buf,
+                         size_t count)
+{
+       struct ipath_devdata *dd = dev_get_drvdata(dev);
+       ssize_t ret;
+       u16 enable = 0;
+
+       ret = ipath_parse_ushort(buf, &enable);
+       if (ret < 0) {
+               ipath_dev_err(dd, "attempt to use non-numeric on enable\n");
+               goto bail;
+       }
+
+       if (enable) {
+               if (!(dd->ipath_flags & IPATH_DISABLED))
+                       goto bail;
+
+               dev_info(dev, "Enabling unit %d\n", dd->ipath_unit);
+               /* same as post-reset */
+               ret = ipath_init_chip(dd, 1);
+               if (ret)
+                       ipath_dev_err(dd, "Failed to enable unit %d\n",
+                                     dd->ipath_unit);
+               else {
+                       dd->ipath_flags &= ~IPATH_DISABLED;
+                       *dd->ipath_statusp &= ~IPATH_STATUS_ADMIN_DISABLED;
+               }
+       }
+       else if (!(dd->ipath_flags & IPATH_DISABLED)) {
+               dev_info(dev, "Disabling unit %d\n", dd->ipath_unit);
+               ipath_shutdown_device(dd);
+               dd->ipath_flags |= IPATH_DISABLED;
+               *dd->ipath_statusp |= IPATH_STATUS_ADMIN_DISABLED;
+       }
+
+bail:
+       return ret;
+}
+
+static DRIVER_ATTR(num_units, S_IRUGO, show_num_units, NULL);
+static DRIVER_ATTR(version, S_IRUGO, show_version, NULL);
+
+static struct attribute *driver_attributes[] = {
+       &driver_attr_num_units.attr,
+       &driver_attr_version.attr,
+       NULL
+};
+
+static struct attribute_group driver_attr_group = {
+       .attrs = driver_attributes
+};
+
+static DEVICE_ATTR(guid, S_IWUSR | S_IRUGO, show_guid, store_guid);
+static DEVICE_ATTR(lid, S_IWUSR | S_IRUGO, show_lid, store_lid);
+static DEVICE_ATTR(link_state, S_IWUSR, NULL, store_link_state);
+static DEVICE_ATTR(mlid, S_IWUSR | S_IRUGO, show_mlid, store_mlid);
+static DEVICE_ATTR(mtu, S_IWUSR | S_IRUGO, show_mtu, store_mtu);
+static DEVICE_ATTR(enabled, S_IWUSR | S_IRUGO, show_enabled, store_enabled);
+static DEVICE_ATTR(nguid, S_IRUGO, show_nguid, NULL);
+static DEVICE_ATTR(reset, S_IWUSR, NULL, store_reset);
+static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL);
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+static DEVICE_ATTR(status_str, S_IRUGO, show_status_str, NULL);
+static DEVICE_ATTR(boardversion, S_IRUGO, show_boardversion, NULL);
+static DEVICE_ATTR(unit, S_IRUGO, show_unit, NULL);
+
+static struct attribute *dev_attributes[] = {
+       &dev_attr_guid.attr,
+       &dev_attr_lid.attr,
+       &dev_attr_link_state.attr,
+       &dev_attr_mlid.attr,
+       &dev_attr_mtu.attr,
+       &dev_attr_nguid.attr,
+       &dev_attr_serial.attr,
+       &dev_attr_status.attr,
+       &dev_attr_status_str.attr,
+       &dev_attr_boardversion.attr,
+       &dev_attr_unit.attr,
+       &dev_attr_enabled.attr,
+       NULL
+};
+
+static struct attribute_group dev_attr_group = {
+       .attrs = dev_attributes
+};
+
+/**
+ * ipath_expose_reset - create a device reset file
+ * @dev: the device structure
+ *
+ * Only expose a file that lets us reset the device after someone
+ * enters diag mode.  A device reset is quite likely to crash the
+ * machine entirely, so we don't want to normally make it
+ * available.
+ */
+int ipath_expose_reset(struct device *dev)
+{
+       return device_create_file(dev, &dev_attr_reset);
+}
+
+int ipath_driver_create_group(struct device_driver *drv)
+{
+       int ret;
+
+       ret = sysfs_create_group(&drv->kobj, &driver_attr_group);
+       if (ret)
+               goto bail;
+
+       ret = sysfs_create_group(&drv->kobj, &driver_stat_attr_group);
+       if (ret)
+               sysfs_remove_group(&drv->kobj, &driver_attr_group);
+
+bail:
+       return ret;
+}
+
+void ipath_driver_remove_group(struct device_driver *drv)
+{
+       sysfs_remove_group(&drv->kobj, &driver_stat_attr_group);
+       sysfs_remove_group(&drv->kobj, &driver_attr_group);
+}
+
+int ipath_device_create_group(struct device *dev, struct ipath_devdata *dd)
+{
+       int ret;
+       char unit[5];
+
+       ret = sysfs_create_group(&dev->kobj, &dev_attr_group);
+       if (ret)
+               goto bail;
+
+       ret = sysfs_create_group(&dev->kobj, &dev_counter_attr_group);
+       if (ret)
+               goto bail_attrs;
+
+       snprintf(unit, sizeof(unit), "%02d", dd->ipath_unit);
+       ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj, unit);
+       if (ret == 0)
+               goto bail;
+
+       sysfs_remove_group(&dev->kobj, &dev_counter_attr_group);
+bail_attrs:
+       sysfs_remove_group(&dev->kobj, &dev_attr_group);
+bail:
+       return ret;
+}
+
+void ipath_device_remove_group(struct device *dev, struct ipath_devdata *dd)
+{
+       char unit[5];
+
+       snprintf(unit, sizeof(unit), "%02d", dd->ipath_unit);
+       sysfs_remove_link(&dev->driver->kobj, unit);
+
+       sysfs_remove_group(&dev->kobj, &dev_counter_attr_group);
+       sysfs_remove_group(&dev->kobj, &dev_attr_group);
+
+       device_remove_file(dev, &dev_attr_reset);
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_uc.c b/drivers/infiniband/hw/ipath/ipath_uc.c
new file mode 100644 (file)
index 0000000..0d6dbc0
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ipath_verbs.h"
+#include "ips_common.h"
+
+/* cut down ridiculously long IB macro names */
+#define OP(x) IB_OPCODE_UC_##x
+
+static void complete_last_send(struct ipath_qp *qp, struct ipath_swqe *wqe,
+                              struct ib_wc *wc)
+{
+       if (++qp->s_last == qp->s_size)
+               qp->s_last = 0;
+       if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) ||
+           (wqe->wr.send_flags & IB_SEND_SIGNALED)) {
+               wc->wr_id = wqe->wr.wr_id;
+               wc->status = IB_WC_SUCCESS;
+               wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
+               wc->vendor_err = 0;
+               wc->byte_len = wqe->length;
+               wc->qp_num = qp->ibqp.qp_num;
+               wc->src_qp = qp->remote_qpn;
+               wc->pkey_index = 0;
+               wc->slid = qp->remote_ah_attr.dlid;
+               wc->sl = qp->remote_ah_attr.sl;
+               wc->dlid_path_bits = 0;
+               wc->port_num = 0;
+               ipath_cq_enter(to_icq(qp->ibqp.send_cq), wc, 0);
+       }
+       wqe = get_swqe_ptr(qp, qp->s_last);
+}
+
+/**
+ * ipath_do_uc_send - do a send on a UC queue
+ * @data: contains a pointer to the QP to send on
+ *
+ * Process entries in the send work queue until the queue is exhausted.
+ * Only allow one CPU to send a packet per QP (tasklet).
+ * Otherwise, after we drop the QP lock, two threads could send
+ * packets out of order.
+ * This is similar to ipath_do_rc_send() below except we don't have
+ * timeouts or resends.
+ */
+void ipath_do_uc_send(unsigned long data)
+{
+       struct ipath_qp *qp = (struct ipath_qp *)data;
+       struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
+       struct ipath_swqe *wqe;
+       unsigned long flags;
+       u16 lrh0;
+       u32 hwords;
+       u32 nwords;
+       u32 extra_bytes;
+       u32 bth0;
+       u32 bth2;
+       u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+       u32 len;
+       struct ipath_other_headers *ohdr;
+       struct ib_wc wc;
+
+       if (test_and_set_bit(IPATH_S_BUSY, &qp->s_flags))
+               goto bail;
+
+       if (unlikely(qp->remote_ah_attr.dlid ==
+                    ipath_layer_get_lid(dev->dd))) {
+               /* Pass in an uninitialized ib_wc to save stack space. */
+               ipath_ruc_loopback(qp, &wc);
+               clear_bit(IPATH_S_BUSY, &qp->s_flags);
+               goto bail;
+       }
+
+       ohdr = &qp->s_hdr.u.oth;
+       if (qp->remote_ah_attr.ah_flags & IB_AH_GRH)
+               ohdr = &qp->s_hdr.u.l.oth;
+
+again:
+       /* Check for a constructed packet to be sent. */
+       if (qp->s_hdrwords != 0) {
+                       /*
+                        * If no PIO bufs are available, return.
+                        * An interrupt will call ipath_ib_piobufavail()
+                        * when one is available.
+                        */
+                       if (ipath_verbs_send(dev->dd, qp->s_hdrwords,
+                                            (u32 *) &qp->s_hdr,
+                                            qp->s_cur_size,
+                                            qp->s_cur_sge)) {
+                               ipath_no_bufs_available(qp, dev);
+                               goto bail;
+                       }
+                       dev->n_unicast_xmit++;
+               /* Record that we sent the packet and s_hdr is empty. */
+               qp->s_hdrwords = 0;
+       }
+
+       lrh0 = IPS_LRH_BTH;
+       /* header size in 32-bit words LRH+BTH = (8+12)/4. */
+       hwords = 5;
+
+       /*
+        * The lock is needed to synchronize between
+        * setting qp->s_ack_state and post_send().
+        */
+       spin_lock_irqsave(&qp->s_lock, flags);
+
+       if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK))
+               goto done;
+
+       bth0 = ipath_layer_get_pkey(dev->dd, qp->s_pkey_index);
+
+       /* Send a request. */
+       wqe = get_swqe_ptr(qp, qp->s_last);
+       switch (qp->s_state) {
+       default:
+               /*
+                * Signal the completion of the last send (if there is
+                * one).
+                */
+               if (qp->s_last != qp->s_tail)
+                       complete_last_send(qp, wqe, &wc);
+
+               /* Check if send work queue is empty. */
+               if (qp->s_tail == qp->s_head)
+                       goto done;
+               /*
+                * Start a new request.
+                */
+               qp->s_psn = wqe->psn = qp->s_next_psn;
+               qp->s_sge.sge = wqe->sg_list[0];
+               qp->s_sge.sg_list = wqe->sg_list + 1;
+               qp->s_sge.num_sge = wqe->wr.num_sge;
+               qp->s_len = len = wqe->length;
+               switch (wqe->wr.opcode) {
+               case IB_WR_SEND:
+               case IB_WR_SEND_WITH_IMM:
+                       if (len > pmtu) {
+                               qp->s_state = OP(SEND_FIRST);
+                               len = pmtu;
+                               break;
+                       }
+                       if (wqe->wr.opcode == IB_WR_SEND)
+                               qp->s_state = OP(SEND_ONLY);
+                       else {
+                               qp->s_state =
+                                       OP(SEND_ONLY_WITH_IMMEDIATE);
+                               /* Immediate data comes after the BTH */
+                               ohdr->u.imm_data = wqe->wr.imm_data;
+                               hwords += 1;
+                       }
+                       if (wqe->wr.send_flags & IB_SEND_SOLICITED)
+                               bth0 |= 1 << 23;
+                       break;
+
+               case IB_WR_RDMA_WRITE:
+               case IB_WR_RDMA_WRITE_WITH_IMM:
+                       ohdr->u.rc.reth.vaddr =
+                               cpu_to_be64(wqe->wr.wr.rdma.remote_addr);
+                       ohdr->u.rc.reth.rkey =
+                               cpu_to_be32(wqe->wr.wr.rdma.rkey);
+                       ohdr->u.rc.reth.length = cpu_to_be32(len);
+                       hwords += sizeof(struct ib_reth) / 4;
+                       if (len > pmtu) {
+                               qp->s_state = OP(RDMA_WRITE_FIRST);
+                               len = pmtu;
+                               break;
+                       }
+                       if (wqe->wr.opcode == IB_WR_RDMA_WRITE)
+                               qp->s_state = OP(RDMA_WRITE_ONLY);
+                       else {
+                               qp->s_state =
+                                       OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE);
+                               /* Immediate data comes after the RETH */
+                               ohdr->u.rc.imm_data = wqe->wr.imm_data;
+                               hwords += 1;
+                               if (wqe->wr.send_flags & IB_SEND_SOLICITED)
+                                       bth0 |= 1 << 23;
+                       }
+                       break;
+
+               default:
+                       goto done;
+               }
+               if (++qp->s_tail >= qp->s_size)
+                       qp->s_tail = 0;
+               break;
+
+       case OP(SEND_FIRST):
+               qp->s_state = OP(SEND_MIDDLE);
+               /* FALLTHROUGH */
+       case OP(SEND_MIDDLE):
+               len = qp->s_len;
+               if (len > pmtu) {
+                       len = pmtu;
+                       break;
+               }
+               if (wqe->wr.opcode == IB_WR_SEND)
+                       qp->s_state = OP(SEND_LAST);
+               else {
+                       qp->s_state = OP(SEND_LAST_WITH_IMMEDIATE);
+                       /* Immediate data comes after the BTH */
+                       ohdr->u.imm_data = wqe->wr.imm_data;
+                       hwords += 1;
+               }
+               if (wqe->wr.send_flags & IB_SEND_SOLICITED)
+                       bth0 |= 1 << 23;
+               break;
+
+       case OP(RDMA_WRITE_FIRST):
+               qp->s_state = OP(RDMA_WRITE_MIDDLE);
+               /* FALLTHROUGH */
+       case OP(RDMA_WRITE_MIDDLE):
+               len = qp->s_len;
+               if (len > pmtu) {
+                       len = pmtu;
+                       break;
+               }
+               if (wqe->wr.opcode == IB_WR_RDMA_WRITE)
+                       qp->s_state = OP(RDMA_WRITE_LAST);
+               else {
+                       qp->s_state =
+                               OP(RDMA_WRITE_LAST_WITH_IMMEDIATE);
+                       /* Immediate data comes after the BTH */
+                       ohdr->u.imm_data = wqe->wr.imm_data;
+                       hwords += 1;
+                       if (wqe->wr.send_flags & IB_SEND_SOLICITED)
+                               bth0 |= 1 << 23;
+               }
+               break;
+       }
+       bth2 = qp->s_next_psn++ & IPS_PSN_MASK;
+       qp->s_len -= len;
+       bth0 |= qp->s_state << 24;
+
+       spin_unlock_irqrestore(&qp->s_lock, flags);
+
+       /* Construct the header. */
+       extra_bytes = (4 - len) & 3;
+       nwords = (len + extra_bytes) >> 2;
+       if (unlikely(qp->remote_ah_attr.ah_flags & IB_AH_GRH)) {
+               /* Header size in 32-bit words. */
+               hwords += 10;
+               lrh0 = IPS_LRH_GRH;
+               qp->s_hdr.u.l.grh.version_tclass_flow =
+                       cpu_to_be32((6 << 28) |
+                                   (qp->remote_ah_attr.grh.traffic_class
+                                    << 20) |
+                                   qp->remote_ah_attr.grh.flow_label);
+               qp->s_hdr.u.l.grh.paylen =
+                       cpu_to_be16(((hwords - 12) + nwords +
+                                    SIZE_OF_CRC) << 2);
+               /* next_hdr is defined by C8-7 in ch. 8.4.1 */
+               qp->s_hdr.u.l.grh.next_hdr = 0x1B;
+               qp->s_hdr.u.l.grh.hop_limit =
+                       qp->remote_ah_attr.grh.hop_limit;
+               /* The SGID is 32-bit aligned. */
+               qp->s_hdr.u.l.grh.sgid.global.subnet_prefix =
+                       dev->gid_prefix;
+               qp->s_hdr.u.l.grh.sgid.global.interface_id =
+                       ipath_layer_get_guid(dev->dd);
+               qp->s_hdr.u.l.grh.dgid = qp->remote_ah_attr.grh.dgid;
+       }
+       qp->s_hdrwords = hwords;
+       qp->s_cur_sge = &qp->s_sge;
+       qp->s_cur_size = len;
+       lrh0 |= qp->remote_ah_attr.sl << 4;
+       qp->s_hdr.lrh[0] = cpu_to_be16(lrh0);
+       /* DEST LID */
+       qp->s_hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid);
+       qp->s_hdr.lrh[2] = cpu_to_be16(hwords + nwords + SIZE_OF_CRC);
+       qp->s_hdr.lrh[3] = cpu_to_be16(ipath_layer_get_lid(dev->dd));
+       bth0 |= extra_bytes << 20;
+       ohdr->bth[0] = cpu_to_be32(bth0);
+       ohdr->bth[1] = cpu_to_be32(qp->remote_qpn);
+       ohdr->bth[2] = cpu_to_be32(bth2);
+
+       /* Check for more work to do. */
+       goto again;
+
+done:
+       spin_unlock_irqrestore(&qp->s_lock, flags);
+       clear_bit(IPATH_S_BUSY, &qp->s_flags);
+
+bail:
+       return;
+}
+
+/**
+ * ipath_uc_rcv - handle an incoming UC packet
+ * @dev: the device the packet came in on
+ * @hdr: the header of the packet
+ * @has_grh: true if the packet has a GRH
+ * @data: the packet data
+ * @tlen: the length of the packet
+ * @qp: the QP for this packet.
+ *
+ * This is called from ipath_qp_rcv() to process an incoming UC packet
+ * for the given QP.
+ * Called at interrupt level.
+ */
+void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
+                 int has_grh, void *data, u32 tlen, struct ipath_qp *qp)
+{
+       struct ipath_other_headers *ohdr;
+       int opcode;
+       u32 hdrsize;
+       u32 psn;
+       u32 pad;
+       unsigned long flags;
+       struct ib_wc wc;
+       u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
+       struct ib_reth *reth;
+       int header_in_data;
+
+       /* Check for GRH */
+       if (!has_grh) {
+               ohdr = &hdr->u.oth;
+               hdrsize = 8 + 12;       /* LRH + BTH */
+               psn = be32_to_cpu(ohdr->bth[2]);
+               header_in_data = 0;
+       } else {
+               ohdr = &hdr->u.l.oth;
+               hdrsize = 8 + 40 + 12;  /* LRH + GRH + BTH */
+               /*
+                * The header with GRH is 60 bytes and the
+                * core driver sets the eager header buffer
+                * size to 56 bytes so the last 4 bytes of
+                * the BTH header (PSN) is in the data buffer.
+                */
+               header_in_data =
+                       ipath_layer_get_rcvhdrentsize(dev->dd) == 16;
+               if (header_in_data) {
+                       psn = be32_to_cpu(((__be32 *) data)[0]);
+                       data += sizeof(__be32);
+               } else
+                       psn = be32_to_cpu(ohdr->bth[2]);
+       }
+       /*
+        * The opcode is in the low byte when its in network order
+        * (top byte when in host order).
+        */
+       opcode = be32_to_cpu(ohdr->bth[0]) >> 24;
+
+       wc.imm_data = 0;
+       wc.wc_flags = 0;
+
+       spin_lock_irqsave(&qp->r_rq.lock, flags);
+
+       /* Compare the PSN verses the expected PSN. */
+       if (unlikely(ipath_cmp24(psn, qp->r_psn) != 0)) {
+               /*
+                * Handle a sequence error.
+                * Silently drop any current message.
+                */
+               qp->r_psn = psn;
+       inv:
+               qp->r_state = OP(SEND_LAST);
+               switch (opcode) {
+               case OP(SEND_FIRST):
+               case OP(SEND_ONLY):
+               case OP(SEND_ONLY_WITH_IMMEDIATE):
+                       goto send_first;
+
+               case OP(RDMA_WRITE_FIRST):
+               case OP(RDMA_WRITE_ONLY):
+               case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE):
+                       goto rdma_first;
+
+               default:
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+       }
+
+       /* Check for opcode sequence errors. */
+       switch (qp->r_state) {
+       case OP(SEND_FIRST):
+       case OP(SEND_MIDDLE):
+               if (opcode == OP(SEND_MIDDLE) ||
+                   opcode == OP(SEND_LAST) ||
+                   opcode == OP(SEND_LAST_WITH_IMMEDIATE))
+                       break;
+               goto inv;
+
+       case OP(RDMA_WRITE_FIRST):
+       case OP(RDMA_WRITE_MIDDLE):
+               if (opcode == OP(RDMA_WRITE_MIDDLE) ||
+                   opcode == OP(RDMA_WRITE_LAST) ||
+                   opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE))
+                       break;
+               goto inv;
+
+       default:
+               if (opcode == OP(SEND_FIRST) ||
+                   opcode == OP(SEND_ONLY) ||
+                   opcode == OP(SEND_ONLY_WITH_IMMEDIATE) ||
+                   opcode == OP(RDMA_WRITE_FIRST) ||
+                   opcode == OP(RDMA_WRITE_ONLY) ||
+                   opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE))
+                       break;
+               goto inv;
+       }
+
+       /* OK, process the packet. */
+       switch (opcode) {
+       case OP(SEND_FIRST):
+       case OP(SEND_ONLY):
+       case OP(SEND_ONLY_WITH_IMMEDIATE):
+       send_first:
+               if (qp->r_reuse_sge) {
+                       qp->r_reuse_sge = 0;
+                       qp->r_sge = qp->s_rdma_sge;
+               } else if (!ipath_get_rwqe(qp, 0)) {
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               /* Save the WQE so we can reuse it in case of an error. */
+               qp->s_rdma_sge = qp->r_sge;
+               qp->r_rcv_len = 0;
+               if (opcode == OP(SEND_ONLY))
+                       goto send_last;
+               else if (opcode == OP(SEND_ONLY_WITH_IMMEDIATE))
+                       goto send_last_imm;
+               /* FALLTHROUGH */
+       case OP(SEND_MIDDLE):
+               /* Check for invalid length PMTU or posted rwqe len. */
+               if (unlikely(tlen != (hdrsize + pmtu + 4))) {
+                       qp->r_reuse_sge = 1;
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               qp->r_rcv_len += pmtu;
+               if (unlikely(qp->r_rcv_len > qp->r_len)) {
+                       qp->r_reuse_sge = 1;
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               ipath_copy_sge(&qp->r_sge, data, pmtu);
+               break;
+
+       case OP(SEND_LAST_WITH_IMMEDIATE):
+       send_last_imm:
+               if (header_in_data) {
+                       wc.imm_data = *(__be32 *) data;
+                       data += sizeof(__be32);
+               } else {
+                       /* Immediate data comes after BTH */
+                       wc.imm_data = ohdr->u.imm_data;
+               }
+               hdrsize += 4;
+               wc.wc_flags = IB_WC_WITH_IMM;
+               /* FALLTHROUGH */
+       case OP(SEND_LAST):
+       send_last:
+               /* Get the number of bytes the message was padded by. */
+               pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
+               /* Check for invalid length. */
+               /* XXX LAST len should be >= 1 */
+               if (unlikely(tlen < (hdrsize + pad + 4))) {
+                       qp->r_reuse_sge = 1;
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               /* Don't count the CRC. */
+               tlen -= (hdrsize + pad + 4);
+               wc.byte_len = tlen + qp->r_rcv_len;
+               if (unlikely(wc.byte_len > qp->r_len)) {
+                       qp->r_reuse_sge = 1;
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               /* XXX Need to free SGEs */
+       last_imm:
+               ipath_copy_sge(&qp->r_sge, data, tlen);
+               wc.wr_id = qp->r_wr_id;
+               wc.status = IB_WC_SUCCESS;
+               wc.opcode = IB_WC_RECV;
+               wc.vendor_err = 0;
+               wc.qp_num = qp->ibqp.qp_num;
+               wc.src_qp = qp->remote_qpn;
+               wc.pkey_index = 0;
+               wc.slid = qp->remote_ah_attr.dlid;
+               wc.sl = qp->remote_ah_attr.sl;
+               wc.dlid_path_bits = 0;
+               wc.port_num = 0;
+               /* Signal completion event if the solicited bit is set. */
+               ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
+                              (ohdr->bth[0] &
+                               __constant_cpu_to_be32(1 << 23)) != 0);
+               break;
+
+       case OP(RDMA_WRITE_FIRST):
+       case OP(RDMA_WRITE_ONLY):
+       case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE): /* consume RWQE */
+       rdma_first:
+               /* RETH comes after BTH */
+               if (!header_in_data)
+                       reth = &ohdr->u.rc.reth;
+               else {
+                       reth = (struct ib_reth *)data;
+                       data += sizeof(*reth);
+               }
+               hdrsize += sizeof(*reth);
+               qp->r_len = be32_to_cpu(reth->length);
+               qp->r_rcv_len = 0;
+               if (qp->r_len != 0) {
+                       u32 rkey = be32_to_cpu(reth->rkey);
+                       u64 vaddr = be64_to_cpu(reth->vaddr);
+
+                       /* Check rkey */
+                       if (unlikely(!ipath_rkey_ok(
+                                            dev, &qp->r_sge, qp->r_len,
+                                            vaddr, rkey,
+                                            IB_ACCESS_REMOTE_WRITE))) {
+                               dev->n_pkt_drops++;
+                               goto done;
+                       }
+               } else {
+                       qp->r_sge.sg_list = NULL;
+                       qp->r_sge.sge.mr = NULL;
+                       qp->r_sge.sge.vaddr = NULL;
+                       qp->r_sge.sge.length = 0;
+                       qp->r_sge.sge.sge_length = 0;
+               }
+               if (unlikely(!(qp->qp_access_flags &
+                              IB_ACCESS_REMOTE_WRITE))) {
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               if (opcode == OP(RDMA_WRITE_ONLY))
+                       goto rdma_last;
+               else if (opcode ==
+                        OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE))
+                       goto rdma_last_imm;
+               /* FALLTHROUGH */
+       case OP(RDMA_WRITE_MIDDLE):
+               /* Check for invalid length PMTU or posted rwqe len. */
+               if (unlikely(tlen != (hdrsize + pmtu + 4))) {
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               qp->r_rcv_len += pmtu;
+               if (unlikely(qp->r_rcv_len > qp->r_len)) {
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               ipath_copy_sge(&qp->r_sge, data, pmtu);
+               break;
+
+       case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE):
+       rdma_last_imm:
+               /* Get the number of bytes the message was padded by. */
+               pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
+               /* Check for invalid length. */
+               /* XXX LAST len should be >= 1 */
+               if (unlikely(tlen < (hdrsize + pad + 4))) {
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               /* Don't count the CRC. */
+               tlen -= (hdrsize + pad + 4);
+               if (unlikely(tlen + qp->r_rcv_len != qp->r_len)) {
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               if (qp->r_reuse_sge) {
+                       qp->r_reuse_sge = 0;
+               } else if (!ipath_get_rwqe(qp, 1)) {
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               if (header_in_data) {
+                       wc.imm_data = *(__be32 *) data;
+                       data += sizeof(__be32);
+               } else {
+                       /* Immediate data comes after BTH */
+                       wc.imm_data = ohdr->u.imm_data;
+               }
+               hdrsize += 4;
+               wc.wc_flags = IB_WC_WITH_IMM;
+               wc.byte_len = 0;
+               goto last_imm;
+
+       case OP(RDMA_WRITE_LAST):
+       rdma_last:
+               /* Get the number of bytes the message was padded by. */
+               pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
+               /* Check for invalid length. */
+               /* XXX LAST len should be >= 1 */
+               if (unlikely(tlen < (hdrsize + pad + 4))) {
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               /* Don't count the CRC. */
+               tlen -= (hdrsize + pad + 4);
+               if (unlikely(tlen + qp->r_rcv_len != qp->r_len)) {
+                       dev->n_pkt_drops++;
+                       goto done;
+               }
+               ipath_copy_sge(&qp->r_sge, data, tlen);
+               break;
+
+       default:
+               /* Drop packet for unknown opcodes. */
+               spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+               dev->n_pkt_drops++;
+               goto bail;
+       }
+       qp->r_psn++;
+       qp->r_state = opcode;
+done:
+       spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+
+bail:
+       return;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c
new file mode 100644 (file)
index 0000000..5ff3de6
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <rdma/ib_smi.h>
+
+#include "ipath_verbs.h"
+#include "ips_common.h"
+
+/**
+ * ipath_ud_loopback - handle send on loopback QPs
+ * @sqp: the QP
+ * @ss: the SGE state
+ * @length: the length of the data to send
+ * @wr: the work request
+ * @wc: the work completion entry
+ *
+ * This is called from ipath_post_ud_send() to forward a WQE addressed
+ * to the same HCA.
+ */
+void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_sge_state *ss,
+                      u32 length, struct ib_send_wr *wr, struct ib_wc *wc)
+{
+       struct ipath_ibdev *dev = to_idev(sqp->ibqp.device);
+       struct ipath_qp *qp;
+       struct ib_ah_attr *ah_attr;
+       unsigned long flags;
+       struct ipath_rq *rq;
+       struct ipath_srq *srq;
+       struct ipath_sge_state rsge;
+       struct ipath_sge *sge;
+       struct ipath_rwqe *wqe;
+
+       qp = ipath_lookup_qpn(&dev->qp_table, wr->wr.ud.remote_qpn);
+       if (!qp)
+               return;
+
+       /*
+        * Check that the qkey matches (except for QP0, see 9.6.1.4.1).
+        * Qkeys with the high order bit set mean use the
+        * qkey from the QP context instead of the WR (see 10.2.5).
+        */
+       if (unlikely(qp->ibqp.qp_num &&
+                    ((int) wr->wr.ud.remote_qkey < 0
+                     ? qp->qkey : wr->wr.ud.remote_qkey) != qp->qkey)) {
+               /* XXX OK to lose a count once in a while. */
+               dev->qkey_violations++;
+               dev->n_pkt_drops++;
+               goto done;
+       }
+
+       /*
+        * A GRH is expected to preceed the data even if not
+        * present on the wire.
+        */
+       wc->byte_len = length + sizeof(struct ib_grh);
+
+       if (wr->opcode == IB_WR_SEND_WITH_IMM) {
+               wc->wc_flags = IB_WC_WITH_IMM;
+               wc->imm_data = wr->imm_data;
+       } else {
+               wc->wc_flags = 0;
+               wc->imm_data = 0;
+       }
+
+       /*
+        * Get the next work request entry to find where to put the data.
+        * Note that it is safe to drop the lock after changing rq->tail
+        * since ipath_post_receive() won't fill the empty slot.
+        */
+       if (qp->ibqp.srq) {
+               srq = to_isrq(qp->ibqp.srq);
+               rq = &srq->rq;
+       } else {
+               srq = NULL;
+               rq = &qp->r_rq;
+       }
+       spin_lock_irqsave(&rq->lock, flags);
+       if (rq->tail == rq->head) {
+               spin_unlock_irqrestore(&rq->lock, flags);
+               dev->n_pkt_drops++;
+               goto done;
+       }
+       /* Silently drop packets which are too big. */
+       wqe = get_rwqe_ptr(rq, rq->tail);
+       if (wc->byte_len > wqe->length) {
+               spin_unlock_irqrestore(&rq->lock, flags);
+               dev->n_pkt_drops++;
+               goto done;
+       }
+       wc->wr_id = wqe->wr_id;
+       rsge.sge = wqe->sg_list[0];
+       rsge.sg_list = wqe->sg_list + 1;
+       rsge.num_sge = wqe->num_sge;
+       if (++rq->tail >= rq->size)
+               rq->tail = 0;
+       if (srq && srq->ibsrq.event_handler) {
+               u32 n;
+
+               if (rq->head < rq->tail)
+                       n = rq->size + rq->head - rq->tail;
+               else
+                       n = rq->head - rq->tail;
+               if (n < srq->limit) {
+                       struct ib_event ev;
+
+                       srq->limit = 0;
+                       spin_unlock_irqrestore(&rq->lock, flags);
+                       ev.device = qp->ibqp.device;
+                       ev.element.srq = qp->ibqp.srq;
+                       ev.event = IB_EVENT_SRQ_LIMIT_REACHED;
+                       srq->ibsrq.event_handler(&ev,
+                                                srq->ibsrq.srq_context);
+               } else
+                       spin_unlock_irqrestore(&rq->lock, flags);
+       } else
+               spin_unlock_irqrestore(&rq->lock, flags);
+       ah_attr = &to_iah(wr->wr.ud.ah)->attr;
+       if (ah_attr->ah_flags & IB_AH_GRH) {
+               ipath_copy_sge(&rsge, &ah_attr->grh, sizeof(struct ib_grh));
+               wc->wc_flags |= IB_WC_GRH;
+       } else
+               ipath_skip_sge(&rsge, sizeof(struct ib_grh));
+       sge = &ss->sge;
+       while (length) {
+               u32 len = sge->length;
+
+               if (len > length)
+                       len = length;
+               BUG_ON(len == 0);
+               ipath_copy_sge(&rsge, sge->vaddr, len);
+               sge->vaddr += len;
+               sge->length -= len;
+               sge->sge_length -= len;
+               if (sge->sge_length == 0) {
+                       if (--ss->num_sge)
+                               *sge = *ss->sg_list++;
+               } else if (sge->length == 0 && sge->mr != NULL) {
+                       if (++sge->n >= IPATH_SEGSZ) {
+                               if (++sge->m >= sge->mr->mapsz)
+                                       break;
+                               sge->n = 0;
+                       }
+                       sge->vaddr =
+                               sge->mr->map[sge->m]->segs[sge->n].vaddr;
+                       sge->length =
+                               sge->mr->map[sge->m]->segs[sge->n].length;
+               }
+               length -= len;
+       }
+       wc->status = IB_WC_SUCCESS;
+       wc->opcode = IB_WC_RECV;
+       wc->vendor_err = 0;
+       wc->qp_num = qp->ibqp.qp_num;
+       wc->src_qp = sqp->ibqp.qp_num;
+       /* XXX do we know which pkey matched? Only needed for GSI. */
+       wc->pkey_index = 0;
+       wc->slid = ipath_layer_get_lid(dev->dd) |
+               (ah_attr->src_path_bits &
+                ((1 << (dev->mkeyprot_resv_lmc & 7)) - 1));
+       wc->sl = ah_attr->sl;
+       wc->dlid_path_bits =
+               ah_attr->dlid & ((1 << (dev->mkeyprot_resv_lmc & 7)) - 1);
+       /* Signal completion event if the solicited bit is set. */
+       ipath_cq_enter(to_icq(qp->ibqp.recv_cq), wc,
+                      wr->send_flags & IB_SEND_SOLICITED);
+
+done:
+       if (atomic_dec_and_test(&qp->refcount))
+               wake_up(&qp->wait);
+}
+
+/**
+ * ipath_post_ud_send - post a UD send on QP
+ * @qp: the QP
+ * @wr: the work request
+ *
+ * Note that we actually send the data as it is posted instead of putting
+ * the request into a ring buffer.  If we wanted to use a ring buffer,
+ * we would need to save a reference to the destination address in the SWQE.
+ */
+int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr)
+{
+       struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
+       struct ipath_other_headers *ohdr;
+       struct ib_ah_attr *ah_attr;
+       struct ipath_sge_state ss;
+       struct ipath_sge *sg_list;
+       struct ib_wc wc;
+       u32 hwords;
+       u32 nwords;
+       u32 len;
+       u32 extra_bytes;
+       u32 bth0;
+       u16 lrh0;
+       u16 lid;
+       int i;
+       int ret;
+
+       if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK)) {
+               ret = 0;
+               goto bail;
+       }
+
+       /* IB spec says that num_sge == 0 is OK. */
+       if (wr->num_sge > qp->s_max_sge) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       if (wr->num_sge > 1) {
+               sg_list = kmalloc((qp->s_max_sge - 1) * sizeof(*sg_list),
+                                 GFP_ATOMIC);
+               if (!sg_list) {
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+       } else
+               sg_list = NULL;
+
+       /* Check the buffer to send. */
+       ss.sg_list = sg_list;
+       ss.sge.mr = NULL;
+       ss.sge.vaddr = NULL;
+       ss.sge.length = 0;
+       ss.sge.sge_length = 0;
+       ss.num_sge = 0;
+       len = 0;
+       for (i = 0; i < wr->num_sge; i++) {
+               /* Check LKEY */
+               if (to_ipd(qp->ibqp.pd)->user && wr->sg_list[i].lkey == 0) {
+                       ret = -EINVAL;
+                       goto bail;
+               }
+
+               if (wr->sg_list[i].length == 0)
+                       continue;
+               if (!ipath_lkey_ok(&dev->lk_table, ss.num_sge ?
+                                  sg_list + ss.num_sge - 1 : &ss.sge,
+                                  &wr->sg_list[i], 0)) {
+                       ret = -EINVAL;
+                       goto bail;
+               }
+               len += wr->sg_list[i].length;
+               ss.num_sge++;
+       }
+       extra_bytes = (4 - len) & 3;
+       nwords = (len + extra_bytes) >> 2;
+
+       /* Construct the header. */
+       ah_attr = &to_iah(wr->wr.ud.ah)->attr;
+       if (ah_attr->dlid == 0) {
+               ret = -EINVAL;
+               goto bail;
+       }
+       if (ah_attr->dlid >= IPS_MULTICAST_LID_BASE) {
+               if (ah_attr->dlid != IPS_PERMISSIVE_LID)
+                       dev->n_multicast_xmit++;
+               else
+                       dev->n_unicast_xmit++;
+       } else {
+               dev->n_unicast_xmit++;
+               lid = ah_attr->dlid &
+                       ~((1 << (dev->mkeyprot_resv_lmc & 7)) - 1);
+               if (unlikely(lid == ipath_layer_get_lid(dev->dd))) {
+                       /*
+                        * Pass in an uninitialized ib_wc to save stack
+                        * space.
+                        */
+                       ipath_ud_loopback(qp, &ss, len, wr, &wc);
+                       goto done;
+               }
+       }
+       if (ah_attr->ah_flags & IB_AH_GRH) {
+               /* Header size in 32-bit words. */
+               hwords = 17;
+               lrh0 = IPS_LRH_GRH;
+               ohdr = &qp->s_hdr.u.l.oth;
+               qp->s_hdr.u.l.grh.version_tclass_flow =
+                       cpu_to_be32((6 << 28) |
+                                   (ah_attr->grh.traffic_class << 20) |
+                                   ah_attr->grh.flow_label);
+               qp->s_hdr.u.l.grh.paylen =
+                       cpu_to_be16(((wr->opcode ==
+                                     IB_WR_SEND_WITH_IMM ? 6 : 5) +
+                                    nwords + SIZE_OF_CRC) << 2);
+               /* next_hdr is defined by C8-7 in ch. 8.4.1 */
+               qp->s_hdr.u.l.grh.next_hdr = 0x1B;
+               qp->s_hdr.u.l.grh.hop_limit = ah_attr->grh.hop_limit;
+               /* The SGID is 32-bit aligned. */
+               qp->s_hdr.u.l.grh.sgid.global.subnet_prefix =
+                       dev->gid_prefix;
+               qp->s_hdr.u.l.grh.sgid.global.interface_id =
+                       ipath_layer_get_guid(dev->dd);
+               qp->s_hdr.u.l.grh.dgid = ah_attr->grh.dgid;
+               /*
+                * Don't worry about sending to locally attached multicast
+                * QPs.  It is unspecified by the spec. what happens.
+                */
+       } else {
+               /* Header size in 32-bit words. */
+               hwords = 7;
+               lrh0 = IPS_LRH_BTH;
+               ohdr = &qp->s_hdr.u.oth;
+       }
+       if (wr->opcode == IB_WR_SEND_WITH_IMM) {
+               ohdr->u.ud.imm_data = wr->imm_data;
+               wc.imm_data = wr->imm_data;
+               hwords += 1;
+               bth0 = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE << 24;
+       } else if (wr->opcode == IB_WR_SEND) {
+               wc.imm_data = 0;
+               bth0 = IB_OPCODE_UD_SEND_ONLY << 24;
+       } else {
+               ret = -EINVAL;
+               goto bail;
+       }
+       lrh0 |= ah_attr->sl << 4;
+       if (qp->ibqp.qp_type == IB_QPT_SMI)
+               lrh0 |= 0xF000; /* Set VL (see ch. 13.5.3.1) */
+       qp->s_hdr.lrh[0] = cpu_to_be16(lrh0);
+       qp->s_hdr.lrh[1] = cpu_to_be16(ah_attr->dlid);  /* DEST LID */
+       qp->s_hdr.lrh[2] = cpu_to_be16(hwords + nwords + SIZE_OF_CRC);
+       lid = ipath_layer_get_lid(dev->dd);
+       if (lid) {
+               lid |= ah_attr->src_path_bits &
+                       ((1 << (dev->mkeyprot_resv_lmc & 7)) - 1);
+               qp->s_hdr.lrh[3] = cpu_to_be16(lid);
+       } else
+               qp->s_hdr.lrh[3] = IB_LID_PERMISSIVE;
+       if (wr->send_flags & IB_SEND_SOLICITED)
+               bth0 |= 1 << 23;
+       bth0 |= extra_bytes << 20;
+       bth0 |= qp->ibqp.qp_type == IB_QPT_SMI ? IPS_DEFAULT_P_KEY :
+               ipath_layer_get_pkey(dev->dd, qp->s_pkey_index);
+       ohdr->bth[0] = cpu_to_be32(bth0);
+       /*
+        * Use the multicast QP if the destination LID is a multicast LID.
+        */
+       ohdr->bth[1] = ah_attr->dlid >= IPS_MULTICAST_LID_BASE &&
+               ah_attr->dlid != IPS_PERMISSIVE_LID ?
+               __constant_cpu_to_be32(IPS_MULTICAST_QPN) :
+               cpu_to_be32(wr->wr.ud.remote_qpn);
+       /* XXX Could lose a PSN count but not worth locking */
+       ohdr->bth[2] = cpu_to_be32(qp->s_next_psn++ & IPS_PSN_MASK);
+       /*
+        * Qkeys with the high order bit set mean use the
+        * qkey from the QP context instead of the WR (see 10.2.5).
+        */
+       ohdr->u.ud.deth[0] = cpu_to_be32((int)wr->wr.ud.remote_qkey < 0 ?
+                                        qp->qkey : wr->wr.ud.remote_qkey);
+       ohdr->u.ud.deth[1] = cpu_to_be32(qp->ibqp.qp_num);
+       if (ipath_verbs_send(dev->dd, hwords, (u32 *) &qp->s_hdr,
+                            len, &ss))
+               dev->n_no_piobuf++;
+
+done:
+       /* Queue the completion status entry. */
+       if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) ||
+           (wr->send_flags & IB_SEND_SIGNALED)) {
+               wc.wr_id = wr->wr_id;
+               wc.status = IB_WC_SUCCESS;
+               wc.vendor_err = 0;
+               wc.opcode = IB_WC_SEND;
+               wc.byte_len = len;
+               wc.qp_num = qp->ibqp.qp_num;
+               wc.src_qp = 0;
+               wc.wc_flags = 0;
+               /* XXX initialize other fields? */
+               ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 0);
+       }
+       kfree(sg_list);
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_ud_rcv - receive an incoming UD packet
+ * @dev: the device the packet came in on
+ * @hdr: the packet header
+ * @has_grh: true if the packet has a GRH
+ * @data: the packet data
+ * @tlen: the packet length
+ * @qp: the QP the packet came on
+ *
+ * This is called from ipath_qp_rcv() to process an incoming UD packet
+ * for the given QP.
+ * Called at interrupt level.
+ */
+void ipath_ud_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
+                 int has_grh, void *data, u32 tlen, struct ipath_qp *qp)
+{
+       struct ipath_other_headers *ohdr;
+       int opcode;
+       u32 hdrsize;
+       u32 pad;
+       unsigned long flags;
+       struct ib_wc wc;
+       u32 qkey;
+       u32 src_qp;
+       struct ipath_rq *rq;
+       struct ipath_srq *srq;
+       struct ipath_rwqe *wqe;
+       u16 dlid;
+       int header_in_data;
+
+       /* Check for GRH */
+       if (!has_grh) {
+               ohdr = &hdr->u.oth;
+               hdrsize = 8 + 12 + 8;   /* LRH + BTH + DETH */
+               qkey = be32_to_cpu(ohdr->u.ud.deth[0]);
+               src_qp = be32_to_cpu(ohdr->u.ud.deth[1]);
+               header_in_data = 0;
+       } else {
+               ohdr = &hdr->u.l.oth;
+               hdrsize = 8 + 40 + 12 + 8; /* LRH + GRH + BTH + DETH */
+               /*
+                * The header with GRH is 68 bytes and the core driver sets
+                * the eager header buffer size to 56 bytes so the last 12
+                * bytes of the IB header is in the data buffer.
+                */
+               header_in_data =
+                       ipath_layer_get_rcvhdrentsize(dev->dd) == 16;
+               if (header_in_data) {
+                       qkey = be32_to_cpu(((__be32 *) data)[1]);
+                       src_qp = be32_to_cpu(((__be32 *) data)[2]);
+                       data += 12;
+               } else {
+                       qkey = be32_to_cpu(ohdr->u.ud.deth[0]);
+                       src_qp = be32_to_cpu(ohdr->u.ud.deth[1]);
+               }
+       }
+       src_qp &= IPS_QPN_MASK;
+
+       /*
+        * Check that the permissive LID is only used on QP0
+        * and the QKEY matches (see 9.6.1.4.1 and 9.6.1.5.1).
+        */
+       if (qp->ibqp.qp_num) {
+               if (unlikely(hdr->lrh[1] == IB_LID_PERMISSIVE ||
+                            hdr->lrh[3] == IB_LID_PERMISSIVE)) {
+                       dev->n_pkt_drops++;
+                       goto bail;
+               }
+               if (unlikely(qkey != qp->qkey)) {
+                       /* XXX OK to lose a count once in a while. */
+                       dev->qkey_violations++;
+                       dev->n_pkt_drops++;
+                       goto bail;
+               }
+       } else if (hdr->lrh[1] == IB_LID_PERMISSIVE ||
+                  hdr->lrh[3] == IB_LID_PERMISSIVE) {
+               struct ib_smp *smp = (struct ib_smp *) data;
+
+               if (smp->mgmt_class != IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
+                       dev->n_pkt_drops++;
+                       goto bail;
+               }
+       }
+
+       /* Get the number of bytes the message was padded by. */
+       pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
+       if (unlikely(tlen < (hdrsize + pad + 4))) {
+               /* Drop incomplete packets. */
+               dev->n_pkt_drops++;
+               goto bail;
+       }
+       tlen -= hdrsize + pad + 4;
+
+       /* Drop invalid MAD packets (see 13.5.3.1). */
+       if (unlikely((qp->ibqp.qp_num == 0 &&
+                     (tlen != 256 ||
+                      (be16_to_cpu(hdr->lrh[0]) >> 12) != 15)) ||
+                    (qp->ibqp.qp_num == 1 &&
+                     (tlen != 256 ||
+                      (be16_to_cpu(hdr->lrh[0]) >> 12) == 15)))) {
+               dev->n_pkt_drops++;
+               goto bail;
+       }
+
+       /*
+        * A GRH is expected to preceed the data even if not
+        * present on the wire.
+        */
+       wc.byte_len = tlen + sizeof(struct ib_grh);
+
+       /*
+        * The opcode is in the low byte when its in network order
+        * (top byte when in host order).
+        */
+       opcode = be32_to_cpu(ohdr->bth[0]) >> 24;
+       if (qp->ibqp.qp_num > 1 &&
+           opcode == IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE) {
+               if (header_in_data) {
+                       wc.imm_data = *(__be32 *) data;
+                       data += sizeof(__be32);
+               } else
+                       wc.imm_data = ohdr->u.ud.imm_data;
+               wc.wc_flags = IB_WC_WITH_IMM;
+               hdrsize += sizeof(u32);
+       } else if (opcode == IB_OPCODE_UD_SEND_ONLY) {
+               wc.imm_data = 0;
+               wc.wc_flags = 0;
+       } else {
+               dev->n_pkt_drops++;
+               goto bail;
+       }
+
+       /*
+        * Get the next work request entry to find where to put the data.
+        * Note that it is safe to drop the lock after changing rq->tail
+        * since ipath_post_receive() won't fill the empty slot.
+        */
+       if (qp->ibqp.srq) {
+               srq = to_isrq(qp->ibqp.srq);
+               rq = &srq->rq;
+       } else {
+               srq = NULL;
+               rq = &qp->r_rq;
+       }
+       spin_lock_irqsave(&rq->lock, flags);
+       if (rq->tail == rq->head) {
+               spin_unlock_irqrestore(&rq->lock, flags);
+               dev->n_pkt_drops++;
+               goto bail;
+       }
+       /* Silently drop packets which are too big. */
+       wqe = get_rwqe_ptr(rq, rq->tail);
+       if (wc.byte_len > wqe->length) {
+               spin_unlock_irqrestore(&rq->lock, flags);
+               dev->n_pkt_drops++;
+               goto bail;
+       }
+       wc.wr_id = wqe->wr_id;
+       qp->r_sge.sge = wqe->sg_list[0];
+       qp->r_sge.sg_list = wqe->sg_list + 1;
+       qp->r_sge.num_sge = wqe->num_sge;
+       if (++rq->tail >= rq->size)
+               rq->tail = 0;
+       if (srq && srq->ibsrq.event_handler) {
+               u32 n;
+
+               if (rq->head < rq->tail)
+                       n = rq->size + rq->head - rq->tail;
+               else
+                       n = rq->head - rq->tail;
+               if (n < srq->limit) {
+                       struct ib_event ev;
+
+                       srq->limit = 0;
+                       spin_unlock_irqrestore(&rq->lock, flags);
+                       ev.device = qp->ibqp.device;
+                       ev.element.srq = qp->ibqp.srq;
+                       ev.event = IB_EVENT_SRQ_LIMIT_REACHED;
+                       srq->ibsrq.event_handler(&ev,
+                                                srq->ibsrq.srq_context);
+               } else
+                       spin_unlock_irqrestore(&rq->lock, flags);
+       } else
+               spin_unlock_irqrestore(&rq->lock, flags);
+       if (has_grh) {
+               ipath_copy_sge(&qp->r_sge, &hdr->u.l.grh,
+                              sizeof(struct ib_grh));
+               wc.wc_flags |= IB_WC_GRH;
+       } else
+               ipath_skip_sge(&qp->r_sge, sizeof(struct ib_grh));
+       ipath_copy_sge(&qp->r_sge, data,
+                      wc.byte_len - sizeof(struct ib_grh));
+       wc.status = IB_WC_SUCCESS;
+       wc.opcode = IB_WC_RECV;
+       wc.vendor_err = 0;
+       wc.qp_num = qp->ibqp.qp_num;
+       wc.src_qp = src_qp;
+       /* XXX do we know which pkey matched? Only needed for GSI. */
+       wc.pkey_index = 0;
+       wc.slid = be16_to_cpu(hdr->lrh[3]);
+       wc.sl = (be16_to_cpu(hdr->lrh[0]) >> 4) & 0xF;
+       dlid = be16_to_cpu(hdr->lrh[1]);
+       /*
+        * Save the LMC lower bits if the destination LID is a unicast LID.
+        */
+       wc.dlid_path_bits = dlid >= IPS_MULTICAST_LID_BASE ? 0 :
+               dlid & ((1 << (dev->mkeyprot_resv_lmc & 7)) - 1);
+       /* Signal completion event if the solicited bit is set. */
+       ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
+                      (ohdr->bth[0] &
+                       __constant_cpu_to_be32(1 << 23)) != 0);
+
+bail:;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c
new file mode 100644 (file)
index 0000000..2bb08af
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/mm.h>
+#include <linux/device.h>
+
+#include "ipath_kernel.h"
+
+static void __ipath_release_user_pages(struct page **p, size_t num_pages,
+                                  int dirty)
+{
+       size_t i;
+
+       for (i = 0; i < num_pages; i++) {
+               ipath_cdbg(MM, "%lu/%lu put_page %p\n", (unsigned long) i,
+                          (unsigned long) num_pages, p[i]);
+               if (dirty)
+                       set_page_dirty_lock(p[i]);
+               put_page(p[i]);
+       }
+}
+
+/* call with current->mm->mmap_sem held */
+static int __get_user_pages(unsigned long start_page, size_t num_pages,
+                       struct page **p, struct vm_area_struct **vma)
+{
+       unsigned long lock_limit;
+       size_t got;
+       int ret;
+
+#if 0
+       /*
+        * XXX - causes MPI programs to fail, haven't had time to check
+        * yet
+        */
+       if (!capable(CAP_IPC_LOCK)) {
+               ret = -EPERM;
+               goto bail;
+       }
+#endif
+
+       lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >>
+               PAGE_SHIFT;
+
+       if (num_pages > lock_limit) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+
+       ipath_cdbg(VERBOSE, "pin %lx pages from vaddr %lx\n",
+                  (unsigned long) num_pages, start_page);
+
+       for (got = 0; got < num_pages; got += ret) {
+               ret = get_user_pages(current, current->mm,
+                                    start_page + got * PAGE_SIZE,
+                                    num_pages - got, 1, 1,
+                                    p + got, vma);
+               if (ret < 0)
+                       goto bail_release;
+       }
+
+       current->mm->locked_vm += num_pages;
+
+       ret = 0;
+       goto bail;
+
+bail_release:
+       __ipath_release_user_pages(p, got, 0);
+bail:
+       return ret;
+}
+
+/**
+ * ipath_get_user_pages - lock user pages into memory
+ * @start_page: the start page
+ * @num_pages: the number of pages
+ * @p: the output page structures
+ *
+ * This function takes a given start page (page aligned user virtual
+ * address) and pins it and the following specified number of pages.  For
+ * now, num_pages is always 1, but that will probably change at some point
+ * (because caller is doing expected sends on a single virtually contiguous
+ * buffer, so we can do all pages at once).
+ */
+int ipath_get_user_pages(unsigned long start_page, size_t num_pages,
+                        struct page **p)
+{
+       int ret;
+
+       down_write(&current->mm->mmap_sem);
+
+       ret = __get_user_pages(start_page, num_pages, p, NULL);
+
+       up_write(&current->mm->mmap_sem);
+
+       return ret;
+}
+
+/**
+ * ipath_get_user_pages_nocopy - lock a single page for I/O and mark shared
+ * @start_page: the page to lock
+ * @p: the output page structure
+ *
+ * This is similar to ipath_get_user_pages, but it's always one page, and we
+ * mark the page as locked for I/O, and shared.  This is used for the user
+ * process page that contains the destination address for the rcvhdrq tail
+ * update, so we need to have the vma. If we don't do this, the page can be
+ * taken away from us on fork, even if the child never touches it, and then
+ * the user process never sees the tail register updates.
+ */
+int ipath_get_user_pages_nocopy(unsigned long page, struct page **p)
+{
+       struct vm_area_struct *vma;
+       int ret;
+
+       down_write(&current->mm->mmap_sem);
+
+       ret = __get_user_pages(page, 1, p, &vma);
+
+       up_write(&current->mm->mmap_sem);
+
+       return ret;
+}
+
+void ipath_release_user_pages(struct page **p, size_t num_pages)
+{
+       down_write(&current->mm->mmap_sem);
+
+       __ipath_release_user_pages(p, num_pages, 1);
+
+       current->mm->locked_vm -= num_pages;
+
+       up_write(&current->mm->mmap_sem);
+}
+
+struct ipath_user_pages_work {
+       struct work_struct work;
+       struct mm_struct *mm;
+       unsigned long num_pages;
+};
+
+static void user_pages_account(void *ptr)
+{
+       struct ipath_user_pages_work *work = ptr;
+
+       down_write(&work->mm->mmap_sem);
+       work->mm->locked_vm -= work->num_pages;
+       up_write(&work->mm->mmap_sem);
+       mmput(work->mm);
+       kfree(work);
+}
+
+void ipath_release_user_pages_on_close(struct page **p, size_t num_pages)
+{
+       struct ipath_user_pages_work *work;
+       struct mm_struct *mm;
+
+       __ipath_release_user_pages(p, num_pages, 1);
+
+       mm = get_task_mm(current);
+       if (!mm)
+               goto bail;
+
+       work = kmalloc(sizeof(*work), GFP_KERNEL);
+       if (!work)
+               goto bail_mm;
+
+       goto bail;
+
+       INIT_WORK(&work->work, user_pages_account, work);
+       work->mm = mm;
+       work->num_pages = num_pages;
+
+bail_mm:
+       mmput(mm);
+bail:
+       return;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
new file mode 100644 (file)
index 0000000..9f27fd3
--- /dev/null
@@ -0,0 +1,1222 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <rdma/ib_mad.h>
+#include <rdma/ib_user_verbs.h>
+#include <linux/utsname.h>
+
+#include "ipath_kernel.h"
+#include "ipath_verbs.h"
+#include "ips_common.h"
+
+/* Not static, because we don't want the compiler removing it */
+const char ipath_verbs_version[] = "ipath_verbs " IPATH_IDSTR;
+
+unsigned int ib_ipath_qp_table_size = 251;
+module_param_named(qp_table_size, ib_ipath_qp_table_size, uint, S_IRUGO);
+MODULE_PARM_DESC(qp_table_size, "QP table size");
+
+unsigned int ib_ipath_lkey_table_size = 12;
+module_param_named(lkey_table_size, ib_ipath_lkey_table_size, uint,
+                  S_IRUGO);
+MODULE_PARM_DESC(lkey_table_size,
+                "LKEY table size in bits (2^n, 1 <= n <= 23)");
+
+unsigned int ib_ipath_debug;   /* debug mask */
+module_param_named(debug, ib_ipath_debug, uint, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(debug, "Verbs debug mask");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("PathScale <support@pathscale.com>");
+MODULE_DESCRIPTION("Pathscale InfiniPath driver");
+
+const int ib_ipath_state_ops[IB_QPS_ERR + 1] = {
+       [IB_QPS_RESET] = 0,
+       [IB_QPS_INIT] = IPATH_POST_RECV_OK,
+       [IB_QPS_RTR] = IPATH_POST_RECV_OK | IPATH_PROCESS_RECV_OK,
+       [IB_QPS_RTS] = IPATH_POST_RECV_OK | IPATH_PROCESS_RECV_OK |
+           IPATH_POST_SEND_OK | IPATH_PROCESS_SEND_OK,
+       [IB_QPS_SQD] = IPATH_POST_RECV_OK | IPATH_PROCESS_RECV_OK |
+           IPATH_POST_SEND_OK,
+       [IB_QPS_SQE] = IPATH_POST_RECV_OK | IPATH_PROCESS_RECV_OK,
+       [IB_QPS_ERR] = 0,
+};
+
+/*
+ * Translate ib_wr_opcode into ib_wc_opcode.
+ */
+const enum ib_wc_opcode ib_ipath_wc_opcode[] = {
+       [IB_WR_RDMA_WRITE] = IB_WC_RDMA_WRITE,
+       [IB_WR_RDMA_WRITE_WITH_IMM] = IB_WC_RDMA_WRITE,
+       [IB_WR_SEND] = IB_WC_SEND,
+       [IB_WR_SEND_WITH_IMM] = IB_WC_SEND,
+       [IB_WR_RDMA_READ] = IB_WC_RDMA_READ,
+       [IB_WR_ATOMIC_CMP_AND_SWP] = IB_WC_COMP_SWAP,
+       [IB_WR_ATOMIC_FETCH_AND_ADD] = IB_WC_FETCH_ADD
+};
+
+/*
+ * System image GUID.
+ */
+__be64 sys_image_guid;
+
+/**
+ * ipath_copy_sge - copy data to SGE memory
+ * @ss: the SGE state
+ * @data: the data to copy
+ * @length: the length of the data
+ */
+void ipath_copy_sge(struct ipath_sge_state *ss, void *data, u32 length)
+{
+       struct ipath_sge *sge = &ss->sge;
+
+       while (length) {
+               u32 len = sge->length;
+
+               BUG_ON(len == 0);
+               if (len > length)
+                       len = length;
+               memcpy(sge->vaddr, data, len);
+               sge->vaddr += len;
+               sge->length -= len;
+               sge->sge_length -= len;
+               if (sge->sge_length == 0) {
+                       if (--ss->num_sge)
+                               *sge = *ss->sg_list++;
+               } else if (sge->length == 0 && sge->mr != NULL) {
+                       if (++sge->n >= IPATH_SEGSZ) {
+                               if (++sge->m >= sge->mr->mapsz)
+                                       break;
+                               sge->n = 0;
+                       }
+                       sge->vaddr =
+                               sge->mr->map[sge->m]->segs[sge->n].vaddr;
+                       sge->length =
+                               sge->mr->map[sge->m]->segs[sge->n].length;
+               }
+               data += len;
+               length -= len;
+       }
+}
+
+/**
+ * ipath_skip_sge - skip over SGE memory - XXX almost dup of prev func
+ * @ss: the SGE state
+ * @length: the number of bytes to skip
+ */
+void ipath_skip_sge(struct ipath_sge_state *ss, u32 length)
+{
+       struct ipath_sge *sge = &ss->sge;
+
+       while (length > sge->sge_length) {
+               length -= sge->sge_length;
+               ss->sge = *ss->sg_list++;
+       }
+       while (length) {
+               u32 len = sge->length;
+
+               BUG_ON(len == 0);
+               if (len > length)
+                       len = length;
+               sge->vaddr += len;
+               sge->length -= len;
+               sge->sge_length -= len;
+               if (sge->sge_length == 0) {
+                       if (--ss->num_sge)
+                               *sge = *ss->sg_list++;
+               } else if (sge->length == 0 && sge->mr != NULL) {
+                       if (++sge->n >= IPATH_SEGSZ) {
+                               if (++sge->m >= sge->mr->mapsz)
+                                       break;
+                               sge->n = 0;
+                       }
+                       sge->vaddr =
+                               sge->mr->map[sge->m]->segs[sge->n].vaddr;
+                       sge->length =
+                               sge->mr->map[sge->m]->segs[sge->n].length;
+               }
+               length -= len;
+       }
+}
+
+/**
+ * ipath_post_send - post a send on a QP
+ * @ibqp: the QP to post the send on
+ * @wr: the list of work requests to post
+ * @bad_wr: the first bad WR is put here
+ *
+ * This may be called from interrupt context.
+ */
+static int ipath_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                          struct ib_send_wr **bad_wr)
+{
+       struct ipath_qp *qp = to_iqp(ibqp);
+       int err = 0;
+
+       /* Check that state is OK to post send. */
+       if (!(ib_ipath_state_ops[qp->state] & IPATH_POST_SEND_OK)) {
+               *bad_wr = wr;
+               err = -EINVAL;
+               goto bail;
+       }
+
+       for (; wr; wr = wr->next) {
+               switch (qp->ibqp.qp_type) {
+               case IB_QPT_UC:
+               case IB_QPT_RC:
+                       err = ipath_post_rc_send(qp, wr);
+                       break;
+
+               case IB_QPT_SMI:
+               case IB_QPT_GSI:
+               case IB_QPT_UD:
+                       err = ipath_post_ud_send(qp, wr);
+                       break;
+
+               default:
+                       err = -EINVAL;
+               }
+               if (err) {
+                       *bad_wr = wr;
+                       break;
+               }
+       }
+
+bail:
+       return err;
+}
+
+/**
+ * ipath_post_receive - post a receive on a QP
+ * @ibqp: the QP to post the receive on
+ * @wr: the WR to post
+ * @bad_wr: the first bad WR is put here
+ *
+ * This may be called from interrupt context.
+ */
+static int ipath_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+                             struct ib_recv_wr **bad_wr)
+{
+       struct ipath_qp *qp = to_iqp(ibqp);
+       unsigned long flags;
+       int ret;
+
+       /* Check that state is OK to post receive. */
+       if (!(ib_ipath_state_ops[qp->state] & IPATH_POST_RECV_OK)) {
+               *bad_wr = wr;
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       for (; wr; wr = wr->next) {
+               struct ipath_rwqe *wqe;
+               u32 next;
+               int i, j;
+
+               if (wr->num_sge > qp->r_rq.max_sge) {
+                       *bad_wr = wr;
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+
+               spin_lock_irqsave(&qp->r_rq.lock, flags);
+               next = qp->r_rq.head + 1;
+               if (next >= qp->r_rq.size)
+                       next = 0;
+               if (next == qp->r_rq.tail) {
+                       spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+                       *bad_wr = wr;
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+
+               wqe = get_rwqe_ptr(&qp->r_rq, qp->r_rq.head);
+               wqe->wr_id = wr->wr_id;
+               wqe->sg_list[0].mr = NULL;
+               wqe->sg_list[0].vaddr = NULL;
+               wqe->sg_list[0].length = 0;
+               wqe->sg_list[0].sge_length = 0;
+               wqe->length = 0;
+               for (i = 0, j = 0; i < wr->num_sge; i++) {
+                       /* Check LKEY */
+                       if (to_ipd(qp->ibqp.pd)->user &&
+                           wr->sg_list[i].lkey == 0) {
+                               spin_unlock_irqrestore(&qp->r_rq.lock,
+                                                      flags);
+                               *bad_wr = wr;
+                               ret = -EINVAL;
+                               goto bail;
+                       }
+                       if (wr->sg_list[i].length == 0)
+                               continue;
+                       if (!ipath_lkey_ok(
+                                   &to_idev(qp->ibqp.device)->lk_table,
+                                   &wqe->sg_list[j], &wr->sg_list[i],
+                                   IB_ACCESS_LOCAL_WRITE)) {
+                               spin_unlock_irqrestore(&qp->r_rq.lock,
+                                                      flags);
+                               *bad_wr = wr;
+                               ret = -EINVAL;
+                               goto bail;
+                       }
+                       wqe->length += wr->sg_list[i].length;
+                       j++;
+               }
+               wqe->num_sge = j;
+               qp->r_rq.head = next;
+               spin_unlock_irqrestore(&qp->r_rq.lock, flags);
+       }
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_qp_rcv - processing an incoming packet on a QP
+ * @dev: the device the packet came on
+ * @hdr: the packet header
+ * @has_grh: true if the packet has a GRH
+ * @data: the packet data
+ * @tlen: the packet length
+ * @qp: the QP the packet came on
+ *
+ * This is called from ipath_ib_rcv() to process an incoming packet
+ * for the given QP.
+ * Called at interrupt level.
+ */
+static void ipath_qp_rcv(struct ipath_ibdev *dev,
+                        struct ipath_ib_header *hdr, int has_grh,
+                        void *data, u32 tlen, struct ipath_qp *qp)
+{
+       /* Check for valid receive state. */
+       if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK)) {
+               dev->n_pkt_drops++;
+               return;
+       }
+
+       switch (qp->ibqp.qp_type) {
+       case IB_QPT_SMI:
+       case IB_QPT_GSI:
+       case IB_QPT_UD:
+               ipath_ud_rcv(dev, hdr, has_grh, data, tlen, qp);
+               break;
+
+       case IB_QPT_RC:
+               ipath_rc_rcv(dev, hdr, has_grh, data, tlen, qp);
+               break;
+
+       case IB_QPT_UC:
+               ipath_uc_rcv(dev, hdr, has_grh, data, tlen, qp);
+               break;
+
+       default:
+               break;
+       }
+}
+
+/**
+ * ipath_ib_rcv - process and incoming packet
+ * @arg: the device pointer
+ * @rhdr: the header of the packet
+ * @data: the packet data
+ * @tlen: the packet length
+ *
+ * This is called from ipath_kreceive() to process an incoming packet at
+ * interrupt level. Tlen is the length of the header + data + CRC in bytes.
+ */
+static void ipath_ib_rcv(void *arg, void *rhdr, void *data, u32 tlen)
+{
+       struct ipath_ibdev *dev = (struct ipath_ibdev *) arg;
+       struct ipath_ib_header *hdr = rhdr;
+       struct ipath_other_headers *ohdr;
+       struct ipath_qp *qp;
+       u32 qp_num;
+       int lnh;
+       u8 opcode;
+       u16 lid;
+
+       if (unlikely(dev == NULL))
+               goto bail;
+
+       if (unlikely(tlen < 24)) {      /* LRH+BTH+CRC */
+               dev->rcv_errors++;
+               goto bail;
+       }
+
+       /* Check for a valid destination LID (see ch. 7.11.1). */
+       lid = be16_to_cpu(hdr->lrh[1]);
+       if (lid < IPS_MULTICAST_LID_BASE) {
+               lid &= ~((1 << (dev->mkeyprot_resv_lmc & 7)) - 1);
+               if (unlikely(lid != ipath_layer_get_lid(dev->dd))) {
+                       dev->rcv_errors++;
+                       goto bail;
+               }
+       }
+
+       /* Check for GRH */
+       lnh = be16_to_cpu(hdr->lrh[0]) & 3;
+       if (lnh == IPS_LRH_BTH)
+               ohdr = &hdr->u.oth;
+       else if (lnh == IPS_LRH_GRH)
+               ohdr = &hdr->u.l.oth;
+       else {
+               dev->rcv_errors++;
+               goto bail;
+       }
+
+       opcode = be32_to_cpu(ohdr->bth[0]) >> 24;
+       dev->opstats[opcode].n_bytes += tlen;
+       dev->opstats[opcode].n_packets++;
+
+       /* Get the destination QP number. */
+       qp_num = be32_to_cpu(ohdr->bth[1]) & IPS_QPN_MASK;
+       if (qp_num == IPS_MULTICAST_QPN) {
+               struct ipath_mcast *mcast;
+               struct ipath_mcast_qp *p;
+
+               mcast = ipath_mcast_find(&hdr->u.l.grh.dgid);
+               if (mcast == NULL) {
+                       dev->n_pkt_drops++;
+                       goto bail;
+               }
+               dev->n_multicast_rcv++;
+               list_for_each_entry_rcu(p, &mcast->qp_list, list)
+                       ipath_qp_rcv(dev, hdr, lnh == IPS_LRH_GRH, data,
+                                    tlen, p->qp);
+               /*
+                * Notify ipath_multicast_detach() if it is waiting for us
+                * to finish.
+                */
+               if (atomic_dec_return(&mcast->refcount) <= 1)
+                       wake_up(&mcast->wait);
+       } else {
+               qp = ipath_lookup_qpn(&dev->qp_table, qp_num);
+               if (qp) {
+                       dev->n_unicast_rcv++;
+                       ipath_qp_rcv(dev, hdr, lnh == IPS_LRH_GRH, data,
+                                    tlen, qp);
+                       /*
+                        * Notify ipath_destroy_qp() if it is waiting
+                        * for us to finish.
+                        */
+                       if (atomic_dec_and_test(&qp->refcount))
+                               wake_up(&qp->wait);
+               } else
+                       dev->n_pkt_drops++;
+       }
+
+bail:;
+}
+
+/**
+ * ipath_ib_timer - verbs timer
+ * @arg: the device pointer
+ *
+ * This is called from ipath_do_rcv_timer() at interrupt level to check for
+ * QPs which need retransmits and to collect performance numbers.
+ */
+static void ipath_ib_timer(void *arg)
+{
+       struct ipath_ibdev *dev = (struct ipath_ibdev *) arg;
+       struct ipath_qp *resend = NULL;
+       struct ipath_qp *rnr = NULL;
+       struct list_head *last;
+       struct ipath_qp *qp;
+       unsigned long flags;
+
+       if (dev == NULL)
+               return;
+
+       spin_lock_irqsave(&dev->pending_lock, flags);
+       /* Start filling the next pending queue. */
+       if (++dev->pending_index >= ARRAY_SIZE(dev->pending))
+               dev->pending_index = 0;
+       /* Save any requests still in the new queue, they have timed out. */
+       last = &dev->pending[dev->pending_index];
+       while (!list_empty(last)) {
+               qp = list_entry(last->next, struct ipath_qp, timerwait);
+               if (last->next == LIST_POISON1 ||
+                   last->next != &qp->timerwait ||
+                   qp->timerwait.prev != last) {
+                       INIT_LIST_HEAD(last);
+               } else {
+                       list_del(&qp->timerwait);
+                       qp->timerwait.prev = (struct list_head *) resend;
+                       resend = qp;
+                       atomic_inc(&qp->refcount);
+               }
+       }
+       last = &dev->rnrwait;
+       if (!list_empty(last)) {
+               qp = list_entry(last->next, struct ipath_qp, timerwait);
+               if (--qp->s_rnr_timeout == 0) {
+                       do {
+                               if (last->next == LIST_POISON1 ||
+                                   last->next != &qp->timerwait ||
+                                   qp->timerwait.prev != last) {
+                                       INIT_LIST_HEAD(last);
+                                       break;
+                               }
+                               list_del(&qp->timerwait);
+                               qp->timerwait.prev =
+                                       (struct list_head *) rnr;
+                               rnr = qp;
+                               if (list_empty(last))
+                                       break;
+                               qp = list_entry(last->next, struct ipath_qp,
+                                               timerwait);
+                       } while (qp->s_rnr_timeout == 0);
+               }
+       }
+       /*
+        * We should only be in the started state if pma_sample_start != 0
+        */
+       if (dev->pma_sample_status == IB_PMA_SAMPLE_STATUS_STARTED &&
+           --dev->pma_sample_start == 0) {
+               dev->pma_sample_status = IB_PMA_SAMPLE_STATUS_RUNNING;
+               ipath_layer_snapshot_counters(dev->dd, &dev->ipath_sword,
+                                             &dev->ipath_rword,
+                                             &dev->ipath_spkts,
+                                             &dev->ipath_rpkts,
+                                             &dev->ipath_xmit_wait);
+       }
+       if (dev->pma_sample_status == IB_PMA_SAMPLE_STATUS_RUNNING) {
+               if (dev->pma_sample_interval == 0) {
+                       u64 ta, tb, tc, td, te;
+
+                       dev->pma_sample_status = IB_PMA_SAMPLE_STATUS_DONE;
+                       ipath_layer_snapshot_counters(dev->dd, &ta, &tb,
+                                                     &tc, &td, &te);
+
+                       dev->ipath_sword = ta - dev->ipath_sword;
+                       dev->ipath_rword = tb - dev->ipath_rword;
+                       dev->ipath_spkts = tc - dev->ipath_spkts;
+                       dev->ipath_rpkts = td - dev->ipath_rpkts;
+                       dev->ipath_xmit_wait = te - dev->ipath_xmit_wait;
+               }
+               else
+                       dev->pma_sample_interval--;
+       }
+       spin_unlock_irqrestore(&dev->pending_lock, flags);
+
+       /* XXX What if timer fires again while this is running? */
+       for (qp = resend; qp != NULL;
+            qp = (struct ipath_qp *) qp->timerwait.prev) {
+               struct ib_wc wc;
+
+               spin_lock_irqsave(&qp->s_lock, flags);
+               if (qp->s_last != qp->s_tail && qp->state == IB_QPS_RTS) {
+                       dev->n_timeouts++;
+                       ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
+               }
+               spin_unlock_irqrestore(&qp->s_lock, flags);
+
+               /* Notify ipath_destroy_qp() if it is waiting. */
+               if (atomic_dec_and_test(&qp->refcount))
+                       wake_up(&qp->wait);
+       }
+       for (qp = rnr; qp != NULL;
+            qp = (struct ipath_qp *) qp->timerwait.prev)
+               tasklet_hi_schedule(&qp->s_task);
+}
+
+/**
+ * ipath_ib_piobufavail - callback when a PIO buffer is available
+ * @arg: the device pointer
+ *
+ * This is called from ipath_intr() at interrupt level when a PIO buffer is
+ * available after ipath_verbs_send() returned an error that no buffers were
+ * available.  Return 0 if we consumed all the PIO buffers and we still have
+ * QPs waiting for buffers (for now, just do a tasklet_hi_schedule and
+ * return one).
+ */
+static int ipath_ib_piobufavail(void *arg)
+{
+       struct ipath_ibdev *dev = (struct ipath_ibdev *) arg;
+       struct ipath_qp *qp;
+       unsigned long flags;
+
+       if (dev == NULL)
+               goto bail;
+
+       spin_lock_irqsave(&dev->pending_lock, flags);
+       while (!list_empty(&dev->piowait)) {
+               qp = list_entry(dev->piowait.next, struct ipath_qp,
+                               piowait);
+               list_del(&qp->piowait);
+               tasklet_hi_schedule(&qp->s_task);
+       }
+       spin_unlock_irqrestore(&dev->pending_lock, flags);
+
+bail:
+       return 1;
+}
+
+static int ipath_query_device(struct ib_device *ibdev,
+                             struct ib_device_attr *props)
+{
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       u32 vendor, boardrev, majrev, minrev;
+
+       memset(props, 0, sizeof(*props));
+
+       props->device_cap_flags = IB_DEVICE_BAD_PKEY_CNTR |
+               IB_DEVICE_BAD_QKEY_CNTR | IB_DEVICE_SHUTDOWN_PORT |
+               IB_DEVICE_SYS_IMAGE_GUID;
+       ipath_layer_query_device(dev->dd, &vendor, &boardrev,
+                                &majrev, &minrev);
+       props->vendor_id = vendor;
+       props->vendor_part_id = boardrev;
+       props->hw_ver = boardrev << 16 | majrev << 8 | minrev;
+
+       props->sys_image_guid = dev->sys_image_guid;
+
+       props->max_mr_size = ~0ull;
+       props->max_qp = 0xffff;
+       props->max_qp_wr = 0xffff;
+       props->max_sge = 255;
+       props->max_cq = 0xffff;
+       props->max_cqe = 0xffff;
+       props->max_mr = 0xffff;
+       props->max_pd = 0xffff;
+       props->max_qp_rd_atom = 1;
+       props->max_qp_init_rd_atom = 1;
+       /* props->max_res_rd_atom */
+       props->max_srq = 0xffff;
+       props->max_srq_wr = 0xffff;
+       props->max_srq_sge = 255;
+       /* props->local_ca_ack_delay */
+       props->atomic_cap = IB_ATOMIC_HCA;
+       props->max_pkeys = ipath_layer_get_npkeys(dev->dd);
+       props->max_mcast_grp = 0xffff;
+       props->max_mcast_qp_attach = 0xffff;
+       props->max_total_mcast_qp_attach = props->max_mcast_qp_attach *
+               props->max_mcast_grp;
+
+       return 0;
+}
+
+const u8 ipath_cvt_physportstate[16] = {
+       [INFINIPATH_IBCS_LT_STATE_DISABLED] = 3,
+       [INFINIPATH_IBCS_LT_STATE_LINKUP] = 5,
+       [INFINIPATH_IBCS_LT_STATE_POLLACTIVE] = 2,
+       [INFINIPATH_IBCS_LT_STATE_POLLQUIET] = 2,
+       [INFINIPATH_IBCS_LT_STATE_SLEEPDELAY] = 1,
+       [INFINIPATH_IBCS_LT_STATE_SLEEPQUIET] = 1,
+       [INFINIPATH_IBCS_LT_STATE_CFGDEBOUNCE] = 4,
+       [INFINIPATH_IBCS_LT_STATE_CFGRCVFCFG] = 4,
+       [INFINIPATH_IBCS_LT_STATE_CFGWAITRMT] = 4,
+       [INFINIPATH_IBCS_LT_STATE_CFGIDLE] = 4,
+       [INFINIPATH_IBCS_LT_STATE_RECOVERRETRAIN] = 6,
+       [INFINIPATH_IBCS_LT_STATE_RECOVERWAITRMT] = 6,
+       [INFINIPATH_IBCS_LT_STATE_RECOVERIDLE] = 6,
+};
+
+static int ipath_query_port(struct ib_device *ibdev,
+                           u8 port, struct ib_port_attr *props)
+{
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       enum ib_mtu mtu;
+       u16 lid = ipath_layer_get_lid(dev->dd);
+       u64 ibcstat;
+
+       memset(props, 0, sizeof(*props));
+       props->lid = lid ? lid : __constant_be16_to_cpu(IB_LID_PERMISSIVE);
+       props->lmc = dev->mkeyprot_resv_lmc & 7;
+       props->sm_lid = dev->sm_lid;
+       props->sm_sl = dev->sm_sl;
+       ibcstat = ipath_layer_get_lastibcstat(dev->dd);
+       props->state = ((ibcstat >> 4) & 0x3) + 1;
+       /* See phys_state_show() */
+       props->phys_state = ipath_cvt_physportstate[
+               ipath_layer_get_lastibcstat(dev->dd) & 0xf];
+       props->port_cap_flags = dev->port_cap_flags;
+       props->gid_tbl_len = 1;
+       props->max_msg_sz = 4096;
+       props->pkey_tbl_len = ipath_layer_get_npkeys(dev->dd);
+       props->bad_pkey_cntr = ipath_layer_get_cr_errpkey(dev->dd) -
+               dev->n_pkey_violations;
+       props->qkey_viol_cntr = dev->qkey_violations;
+       props->active_width = IB_WIDTH_4X;
+       /* See rate_show() */
+       props->active_speed = 1;        /* Regular 10Mbs speed. */
+       props->max_vl_num = 1;          /* VLCap = VL0 */
+       props->init_type_reply = 0;
+
+       props->max_mtu = IB_MTU_4096;
+       switch (ipath_layer_get_ibmtu(dev->dd)) {
+       case 4096:
+               mtu = IB_MTU_4096;
+               break;
+       case 2048:
+               mtu = IB_MTU_2048;
+               break;
+       case 1024:
+               mtu = IB_MTU_1024;
+               break;
+       case 512:
+               mtu = IB_MTU_512;
+               break;
+       case 256:
+               mtu = IB_MTU_256;
+               break;
+       default:
+               mtu = IB_MTU_2048;
+       }
+       props->active_mtu = mtu;
+       props->subnet_timeout = dev->subnet_timeout;
+
+       return 0;
+}
+
+static int ipath_modify_device(struct ib_device *device,
+                              int device_modify_mask,
+                              struct ib_device_modify *device_modify)
+{
+       int ret;
+
+       if (device_modify_mask & ~(IB_DEVICE_MODIFY_SYS_IMAGE_GUID |
+                                  IB_DEVICE_MODIFY_NODE_DESC)) {
+               ret = -EOPNOTSUPP;
+               goto bail;
+       }
+
+       if (device_modify_mask & IB_DEVICE_MODIFY_NODE_DESC)
+               memcpy(device->node_desc, device_modify->node_desc, 64);
+
+       if (device_modify_mask & IB_DEVICE_MODIFY_SYS_IMAGE_GUID)
+               to_idev(device)->sys_image_guid =
+                       cpu_to_be64(device_modify->sys_image_guid);
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+static int ipath_modify_port(struct ib_device *ibdev,
+                            u8 port, int port_modify_mask,
+                            struct ib_port_modify *props)
+{
+       struct ipath_ibdev *dev = to_idev(ibdev);
+
+       dev->port_cap_flags |= props->set_port_cap_mask;
+       dev->port_cap_flags &= ~props->clr_port_cap_mask;
+       if (port_modify_mask & IB_PORT_SHUTDOWN)
+               ipath_layer_set_linkstate(dev->dd, IPATH_IB_LINKDOWN);
+       if (port_modify_mask & IB_PORT_RESET_QKEY_CNTR)
+               dev->qkey_violations = 0;
+       return 0;
+}
+
+static int ipath_query_gid(struct ib_device *ibdev, u8 port,
+                          int index, union ib_gid *gid)
+{
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       int ret;
+
+       if (index >= 1) {
+               ret = -EINVAL;
+               goto bail;
+       }
+       gid->global.subnet_prefix = dev->gid_prefix;
+       gid->global.interface_id = ipath_layer_get_guid(dev->dd);
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+static struct ib_pd *ipath_alloc_pd(struct ib_device *ibdev,
+                                   struct ib_ucontext *context,
+                                   struct ib_udata *udata)
+{
+       struct ipath_pd *pd;
+       struct ib_pd *ret;
+
+       pd = kmalloc(sizeof *pd, GFP_KERNEL);
+       if (!pd) {
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       /* ib_alloc_pd() will initialize pd->ibpd. */
+       pd->user = udata != NULL;
+
+       ret = &pd->ibpd;
+
+bail:
+       return ret;
+}
+
+static int ipath_dealloc_pd(struct ib_pd *ibpd)
+{
+       struct ipath_pd *pd = to_ipd(ibpd);
+
+       kfree(pd);
+
+       return 0;
+}
+
+/**
+ * ipath_create_ah - create an address handle
+ * @pd: the protection domain
+ * @ah_attr: the attributes of the AH
+ *
+ * This may be called from interrupt context.
+ */
+static struct ib_ah *ipath_create_ah(struct ib_pd *pd,
+                                    struct ib_ah_attr *ah_attr)
+{
+       struct ipath_ah *ah;
+       struct ib_ah *ret;
+
+       /* A multicast address requires a GRH (see ch. 8.4.1). */
+       if (ah_attr->dlid >= IPS_MULTICAST_LID_BASE &&
+           ah_attr->dlid != IPS_PERMISSIVE_LID &&
+           !(ah_attr->ah_flags & IB_AH_GRH)) {
+               ret = ERR_PTR(-EINVAL);
+               goto bail;
+       }
+
+       ah = kmalloc(sizeof *ah, GFP_ATOMIC);
+       if (!ah) {
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       /* ib_create_ah() will initialize ah->ibah. */
+       ah->attr = *ah_attr;
+
+       ret = &ah->ibah;
+
+bail:
+       return ret;
+}
+
+/**
+ * ipath_destroy_ah - destroy an address handle
+ * @ibah: the AH to destroy
+ *
+ * This may be called from interrupt context.
+ */
+static int ipath_destroy_ah(struct ib_ah *ibah)
+{
+       struct ipath_ah *ah = to_iah(ibah);
+
+       kfree(ah);
+
+       return 0;
+}
+
+static int ipath_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr)
+{
+       struct ipath_ah *ah = to_iah(ibah);
+
+       *ah_attr = ah->attr;
+
+       return 0;
+}
+
+static int ipath_query_pkey(struct ib_device *ibdev, u8 port, u16 index,
+                           u16 *pkey)
+{
+       struct ipath_ibdev *dev = to_idev(ibdev);
+       int ret;
+
+       if (index >= ipath_layer_get_npkeys(dev->dd)) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       *pkey = ipath_layer_get_pkey(dev->dd, index);
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+
+/**
+ * ipath_alloc_ucontext - allocate a ucontest
+ * @ibdev: the infiniband device
+ * @udata: not used by the InfiniPath driver
+ */
+
+static struct ib_ucontext *ipath_alloc_ucontext(struct ib_device *ibdev,
+                                               struct ib_udata *udata)
+{
+       struct ipath_ucontext *context;
+       struct ib_ucontext *ret;
+
+       context = kmalloc(sizeof *context, GFP_KERNEL);
+       if (!context) {
+               ret = ERR_PTR(-ENOMEM);
+               goto bail;
+       }
+
+       ret = &context->ibucontext;
+
+bail:
+       return ret;
+}
+
+static int ipath_dealloc_ucontext(struct ib_ucontext *context)
+{
+       kfree(to_iucontext(context));
+       return 0;
+}
+
+static int ipath_verbs_register_sysfs(struct ib_device *dev);
+
+/**
+ * ipath_register_ib_device - register our device with the infiniband core
+ * @unit: the device number to register
+ * @dd: the device data structure
+ * Return the allocated ipath_ibdev pointer or NULL on error.
+ */
+static void *ipath_register_ib_device(int unit, struct ipath_devdata *dd)
+{
+       struct ipath_ibdev *idev;
+       struct ib_device *dev;
+       int ret;
+
+       idev = (struct ipath_ibdev *)ib_alloc_device(sizeof *idev);
+       if (idev == NULL)
+               goto bail;
+
+       dev = &idev->ibdev;
+
+       /* Only need to initialize non-zero fields. */
+       spin_lock_init(&idev->qp_table.lock);
+       spin_lock_init(&idev->lk_table.lock);
+       idev->sm_lid = __constant_be16_to_cpu(IB_LID_PERMISSIVE);
+       /* Set the prefix to the default value (see ch. 4.1.1) */
+       idev->gid_prefix = __constant_cpu_to_be64(0xfe80000000000000ULL);
+
+       ret = ipath_init_qp_table(idev, ib_ipath_qp_table_size);
+       if (ret)
+               goto err_qp;
+
+       /*
+        * The top ib_ipath_lkey_table_size bits are used to index the
+        * table.  The lower 8 bits can be owned by the user (copied from
+        * the LKEY).  The remaining bits act as a generation number or tag.
+        */
+       idev->lk_table.max = 1 << ib_ipath_lkey_table_size;
+       idev->lk_table.table = kzalloc(idev->lk_table.max *
+                                      sizeof(*idev->lk_table.table),
+                                      GFP_KERNEL);
+       if (idev->lk_table.table == NULL) {
+               ret = -ENOMEM;
+               goto err_lk;
+       }
+       spin_lock_init(&idev->pending_lock);
+       INIT_LIST_HEAD(&idev->pending[0]);
+       INIT_LIST_HEAD(&idev->pending[1]);
+       INIT_LIST_HEAD(&idev->pending[2]);
+       INIT_LIST_HEAD(&idev->piowait);
+       INIT_LIST_HEAD(&idev->rnrwait);
+       idev->pending_index = 0;
+       idev->port_cap_flags =
+               IB_PORT_SYS_IMAGE_GUID_SUP | IB_PORT_CLIENT_REG_SUP;
+       idev->pma_counter_select[0] = IB_PMA_PORT_XMIT_DATA;
+       idev->pma_counter_select[1] = IB_PMA_PORT_RCV_DATA;
+       idev->pma_counter_select[2] = IB_PMA_PORT_XMIT_PKTS;
+       idev->pma_counter_select[3] = IB_PMA_PORT_RCV_PKTS;
+       idev->pma_counter_select[5] = IB_PMA_PORT_XMIT_WAIT;
+       idev->link_width_enabled = 3;   /* 1x or 4x */
+
+       /*
+        * The system image GUID is supposed to be the same for all
+        * IB HCAs in a single system but since there can be other
+        * device types in the system, we can't be sure this is unique.
+        */
+       if (!sys_image_guid)
+               sys_image_guid = ipath_layer_get_guid(dd);
+       idev->sys_image_guid = sys_image_guid;
+       idev->ib_unit = unit;
+       idev->dd = dd;
+
+       strlcpy(dev->name, "ipath%d", IB_DEVICE_NAME_MAX);
+       dev->node_guid = ipath_layer_get_guid(dd);
+       dev->uverbs_abi_ver = IPATH_UVERBS_ABI_VERSION;
+       dev->uverbs_cmd_mask =
+               (1ull << IB_USER_VERBS_CMD_GET_CONTEXT)         |
+               (1ull << IB_USER_VERBS_CMD_QUERY_DEVICE)        |
+               (1ull << IB_USER_VERBS_CMD_QUERY_PORT)          |
+               (1ull << IB_USER_VERBS_CMD_ALLOC_PD)            |
+               (1ull << IB_USER_VERBS_CMD_DEALLOC_PD)          |
+               (1ull << IB_USER_VERBS_CMD_CREATE_AH)           |
+               (1ull << IB_USER_VERBS_CMD_DESTROY_AH)          |
+               (1ull << IB_USER_VERBS_CMD_QUERY_AH)            |
+               (1ull << IB_USER_VERBS_CMD_REG_MR)              |
+               (1ull << IB_USER_VERBS_CMD_DEREG_MR)            |
+               (1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL) |
+               (1ull << IB_USER_VERBS_CMD_CREATE_CQ)           |
+               (1ull << IB_USER_VERBS_CMD_RESIZE_CQ)           |
+               (1ull << IB_USER_VERBS_CMD_DESTROY_CQ)          |
+               (1ull << IB_USER_VERBS_CMD_POLL_CQ)             |
+               (1ull << IB_USER_VERBS_CMD_REQ_NOTIFY_CQ)       |
+               (1ull << IB_USER_VERBS_CMD_CREATE_QP)           |
+               (1ull << IB_USER_VERBS_CMD_QUERY_QP)            |
+               (1ull << IB_USER_VERBS_CMD_MODIFY_QP)           |
+               (1ull << IB_USER_VERBS_CMD_DESTROY_QP)          |
+               (1ull << IB_USER_VERBS_CMD_POST_SEND)           |
+               (1ull << IB_USER_VERBS_CMD_POST_RECV)           |
+               (1ull << IB_USER_VERBS_CMD_ATTACH_MCAST)        |
+               (1ull << IB_USER_VERBS_CMD_DETACH_MCAST)        |
+               (1ull << IB_USER_VERBS_CMD_CREATE_SRQ)          |
+               (1ull << IB_USER_VERBS_CMD_MODIFY_SRQ)          |
+               (1ull << IB_USER_VERBS_CMD_QUERY_SRQ)           |
+               (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ)         |
+               (1ull << IB_USER_VERBS_CMD_POST_SRQ_RECV);
+       dev->node_type = IB_NODE_CA;
+       dev->phys_port_cnt = 1;
+       dev->dma_device = ipath_layer_get_device(dd);
+       dev->class_dev.dev = dev->dma_device;
+       dev->query_device = ipath_query_device;
+       dev->modify_device = ipath_modify_device;
+       dev->query_port = ipath_query_port;
+       dev->modify_port = ipath_modify_port;
+       dev->query_pkey = ipath_query_pkey;
+       dev->query_gid = ipath_query_gid;
+       dev->alloc_ucontext = ipath_alloc_ucontext;
+       dev->dealloc_ucontext = ipath_dealloc_ucontext;
+       dev->alloc_pd = ipath_alloc_pd;
+       dev->dealloc_pd = ipath_dealloc_pd;
+       dev->create_ah = ipath_create_ah;
+       dev->destroy_ah = ipath_destroy_ah;
+       dev->query_ah = ipath_query_ah;
+       dev->create_srq = ipath_create_srq;
+       dev->modify_srq = ipath_modify_srq;
+       dev->query_srq = ipath_query_srq;
+       dev->destroy_srq = ipath_destroy_srq;
+       dev->create_qp = ipath_create_qp;
+       dev->modify_qp = ipath_modify_qp;
+       dev->query_qp = ipath_query_qp;
+       dev->destroy_qp = ipath_destroy_qp;
+       dev->post_send = ipath_post_send;
+       dev->post_recv = ipath_post_receive;
+       dev->post_srq_recv = ipath_post_srq_receive;
+       dev->create_cq = ipath_create_cq;
+       dev->destroy_cq = ipath_destroy_cq;
+       dev->resize_cq = ipath_resize_cq;
+       dev->poll_cq = ipath_poll_cq;
+       dev->req_notify_cq = ipath_req_notify_cq;
+       dev->get_dma_mr = ipath_get_dma_mr;
+       dev->reg_phys_mr = ipath_reg_phys_mr;
+       dev->reg_user_mr = ipath_reg_user_mr;
+       dev->dereg_mr = ipath_dereg_mr;
+       dev->alloc_fmr = ipath_alloc_fmr;
+       dev->map_phys_fmr = ipath_map_phys_fmr;
+       dev->unmap_fmr = ipath_unmap_fmr;
+       dev->dealloc_fmr = ipath_dealloc_fmr;
+       dev->attach_mcast = ipath_multicast_attach;
+       dev->detach_mcast = ipath_multicast_detach;
+       dev->process_mad = ipath_process_mad;
+
+       snprintf(dev->node_desc, sizeof(dev->node_desc),
+                IPATH_IDSTR " %s kernel_SMA", system_utsname.nodename);
+
+       ret = ib_register_device(dev);
+       if (ret)
+               goto err_reg;
+
+       if (ipath_verbs_register_sysfs(dev))
+               goto err_class;
+
+       ipath_layer_enable_timer(dd);
+
+       goto bail;
+
+err_class:
+       ib_unregister_device(dev);
+err_reg:
+       kfree(idev->lk_table.table);
+err_lk:
+       kfree(idev->qp_table.table);
+err_qp:
+       ib_dealloc_device(dev);
+       _VERBS_ERROR("ib_ipath%d cannot register verbs (%d)!\n",
+                    unit, -ret);
+       idev = NULL;
+
+bail:
+       return idev;
+}
+
+static void ipath_unregister_ib_device(void *arg)
+{
+       struct ipath_ibdev *dev = (struct ipath_ibdev *) arg;
+       struct ib_device *ibdev = &dev->ibdev;
+
+       ipath_layer_disable_timer(dev->dd);
+
+       ib_unregister_device(ibdev);
+
+       if (!list_empty(&dev->pending[0]) ||
+           !list_empty(&dev->pending[1]) ||
+           !list_empty(&dev->pending[2]))
+               _VERBS_ERROR("ipath%d pending list not empty!\n",
+                            dev->ib_unit);
+       if (!list_empty(&dev->piowait))
+               _VERBS_ERROR("ipath%d piowait list not empty!\n",
+                            dev->ib_unit);
+       if (!list_empty(&dev->rnrwait))
+               _VERBS_ERROR("ipath%d rnrwait list not empty!\n",
+                            dev->ib_unit);
+       if (!ipath_mcast_tree_empty())
+               _VERBS_ERROR("ipath%d multicast table memory leak!\n",
+                            dev->ib_unit);
+       /*
+        * Note that ipath_unregister_ib_device() can be called before all
+        * the QPs are destroyed!
+        */
+       ipath_free_all_qps(&dev->qp_table);
+       kfree(dev->qp_table.table);
+       kfree(dev->lk_table.table);
+       ib_dealloc_device(ibdev);
+}
+
+int __init ipath_verbs_init(void)
+{
+       return ipath_verbs_register(ipath_register_ib_device,
+                                   ipath_unregister_ib_device,
+                                   ipath_ib_piobufavail, ipath_ib_rcv,
+                                   ipath_ib_timer);
+}
+
+void __exit ipath_verbs_cleanup(void)
+{
+       ipath_verbs_unregister();
+}
+
+static ssize_t show_rev(struct class_device *cdev, char *buf)
+{
+        struct ipath_ibdev *dev =
+                container_of(cdev, struct ipath_ibdev, ibdev.class_dev);
+        int vendor, boardrev, majrev, minrev;
+
+        ipath_layer_query_device(dev->dd, &vendor, &boardrev,
+                                 &majrev, &minrev);
+        return sprintf(buf, "%d.%d\n", majrev, minrev);
+}
+
+static ssize_t show_hca(struct class_device *cdev, char *buf)
+{
+        struct ipath_ibdev *dev =
+                container_of(cdev, struct ipath_ibdev, ibdev.class_dev);
+        int ret;
+
+        ret = ipath_layer_get_boardname(dev->dd, buf, 128);
+        if (ret < 0)
+                goto bail;
+        strcat(buf, "\n");
+        ret = strlen(buf);
+
+bail:
+       return ret;
+}
+
+static ssize_t show_stats(struct class_device *cdev, char *buf)
+{
+        struct ipath_ibdev *dev =
+                container_of(cdev, struct ipath_ibdev, ibdev.class_dev);
+        int i;
+        int len;
+
+        len = sprintf(buf,
+                      "RC resends  %d\n"
+                      "RC QACKs    %d\n"
+                      "RC ACKs     %d\n"
+                      "RC SEQ NAKs %d\n"
+                      "RC RDMA seq %d\n"
+                      "RC RNR NAKs %d\n"
+                      "RC OTH NAKs %d\n"
+                      "RC timeouts %d\n"
+                      "RC RDMA dup %d\n"
+                      "piobuf wait %d\n"
+                      "no piobuf   %d\n"
+                      "PKT drops   %d\n"
+                      "WQE errs    %d\n",
+                      dev->n_rc_resends, dev->n_rc_qacks, dev->n_rc_acks,
+                      dev->n_seq_naks, dev->n_rdma_seq, dev->n_rnr_naks,
+                      dev->n_other_naks, dev->n_timeouts,
+                      dev->n_rdma_dup_busy, dev->n_piowait,
+                      dev->n_no_piobuf, dev->n_pkt_drops, dev->n_wqe_errs);
+        for (i = 0; i < ARRAY_SIZE(dev->opstats); i++) {
+               const struct ipath_opcode_stats *si = &dev->opstats[i];
+
+                if (!si->n_packets && !si->n_bytes)
+                        continue;
+                len += sprintf(buf + len, "%02x %llu/%llu\n", i,
+                              (unsigned long long) si->n_packets,
+                               (unsigned long long) si->n_bytes);
+        }
+        return len;
+}
+
+static CLASS_DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL);
+static CLASS_DEVICE_ATTR(hca_type, S_IRUGO, show_hca, NULL);
+static CLASS_DEVICE_ATTR(board_id, S_IRUGO, show_hca, NULL);
+static CLASS_DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL);
+
+static struct class_device_attribute *ipath_class_attributes[] = {
+        &class_device_attr_hw_rev,
+        &class_device_attr_hca_type,
+        &class_device_attr_board_id,
+        &class_device_attr_stats
+};
+
+static int ipath_verbs_register_sysfs(struct ib_device *dev)
+{
+        int i;
+       int ret;
+
+        for (i = 0; i < ARRAY_SIZE(ipath_class_attributes); ++i)
+                if (class_device_create_file(&dev->class_dev,
+                                             ipath_class_attributes[i])) {
+                        ret = 1;
+                       goto bail;
+               }
+
+        ret = 0;
+
+bail:
+       return ret;
+}
+
+module_init(ipath_verbs_init);
+module_exit(ipath_verbs_cleanup);
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h
new file mode 100644 (file)
index 0000000..b824632
--- /dev/null
@@ -0,0 +1,697 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef IPATH_VERBS_H
+#define IPATH_VERBS_H
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <rdma/ib_pack.h>
+
+#include "ipath_layer.h"
+#include "verbs_debug.h"
+
+#define QPN_MAX                 (1 << 24)
+#define QPNMAP_ENTRIES          (QPN_MAX / PAGE_SIZE / BITS_PER_BYTE)
+
+/*
+ * Increment this value if any changes that break userspace ABI
+ * compatibility are made.
+ */
+#define IPATH_UVERBS_ABI_VERSION       1
+
+/*
+ * Define an ib_cq_notify value that is not valid so we know when CQ
+ * notifications are armed.
+ */
+#define IB_CQ_NONE     (IB_CQ_NEXT_COMP + 1)
+
+#define IB_RNR_NAK                     0x20
+#define IB_NAK_PSN_ERROR               0x60
+#define IB_NAK_INVALID_REQUEST         0x61
+#define IB_NAK_REMOTE_ACCESS_ERROR     0x62
+#define IB_NAK_REMOTE_OPERATIONAL_ERROR 0x63
+#define IB_NAK_INVALID_RD_REQUEST      0x64
+
+#define IPATH_POST_SEND_OK             0x01
+#define IPATH_POST_RECV_OK             0x02
+#define IPATH_PROCESS_RECV_OK          0x04
+#define IPATH_PROCESS_SEND_OK          0x08
+
+/* IB Performance Manager status values */
+#define IB_PMA_SAMPLE_STATUS_DONE      0x00
+#define IB_PMA_SAMPLE_STATUS_STARTED   0x01
+#define IB_PMA_SAMPLE_STATUS_RUNNING   0x02
+
+/* Mandatory IB performance counter select values. */
+#define IB_PMA_PORT_XMIT_DATA  __constant_htons(0x0001)
+#define IB_PMA_PORT_RCV_DATA   __constant_htons(0x0002)
+#define IB_PMA_PORT_XMIT_PKTS  __constant_htons(0x0003)
+#define IB_PMA_PORT_RCV_PKTS   __constant_htons(0x0004)
+#define IB_PMA_PORT_XMIT_WAIT  __constant_htons(0x0005)
+
+struct ib_reth {
+       __be64 vaddr;
+       __be32 rkey;
+       __be32 length;
+} __attribute__ ((packed));
+
+struct ib_atomic_eth {
+       __be64 vaddr;
+       __be32 rkey;
+       __be64 swap_data;
+       __be64 compare_data;
+} __attribute__ ((packed));
+
+struct ipath_other_headers {
+       __be32 bth[3];
+       union {
+               struct {
+                       __be32 deth[2];
+                       __be32 imm_data;
+               } ud;
+               struct {
+                       struct ib_reth reth;
+                       __be32 imm_data;
+               } rc;
+               struct {
+                       __be32 aeth;
+                       __be64 atomic_ack_eth;
+               } at;
+               __be32 imm_data;
+               __be32 aeth;
+               struct ib_atomic_eth atomic_eth;
+       } u;
+} __attribute__ ((packed));
+
+/*
+ * Note that UD packets with a GRH header are 8+40+12+8 = 68 bytes
+ * long (72 w/ imm_data).  Only the first 56 bytes of the IB header
+ * will be in the eager header buffer.  The remaining 12 or 16 bytes
+ * are in the data buffer.
+ */
+struct ipath_ib_header {
+       __be16 lrh[4];
+       union {
+               struct {
+                       struct ib_grh grh;
+                       struct ipath_other_headers oth;
+               } l;
+               struct ipath_other_headers oth;
+       } u;
+} __attribute__ ((packed));
+
+/*
+ * There is one struct ipath_mcast for each multicast GID.
+ * All attached QPs are then stored as a list of
+ * struct ipath_mcast_qp.
+ */
+struct ipath_mcast_qp {
+       struct list_head list;
+       struct ipath_qp *qp;
+};
+
+struct ipath_mcast {
+       struct rb_node rb_node;
+       union ib_gid mgid;
+       struct list_head qp_list;
+       wait_queue_head_t wait;
+       atomic_t refcount;
+};
+
+/* Memory region */
+struct ipath_mr {
+       struct ib_mr ibmr;
+       struct ipath_mregion mr;        /* must be last */
+};
+
+/* Fast memory region */
+struct ipath_fmr {
+       struct ib_fmr ibfmr;
+       u8 page_shift;
+       struct ipath_mregion mr;        /* must be last */
+};
+
+/* Protection domain */
+struct ipath_pd {
+       struct ib_pd ibpd;
+       int user;               /* non-zero if created from user space */
+};
+
+/* Address Handle */
+struct ipath_ah {
+       struct ib_ah ibah;
+       struct ib_ah_attr attr;
+};
+
+/*
+ * Quick description of our CQ/QP locking scheme:
+ *
+ * We have one global lock that protects dev->cq/qp_table.  Each
+ * struct ipath_cq/qp also has its own lock.  An individual qp lock
+ * may be taken inside of an individual cq lock.  Both cqs attached to
+ * a qp may be locked, with the send cq locked first.  No other
+ * nesting should be done.
+ *
+ * Each struct ipath_cq/qp also has an atomic_t ref count.  The
+ * pointer from the cq/qp_table to the struct counts as one reference.
+ * This reference also is good for access through the consumer API, so
+ * modifying the CQ/QP etc doesn't need to take another reference.
+ * Access because of a completion being polled does need a reference.
+ *
+ * Finally, each struct ipath_cq/qp has a wait_queue_head_t for the
+ * destroy function to sleep on.
+ *
+ * This means that access from the consumer API requires nothing but
+ * taking the struct's lock.
+ *
+ * Access because of a completion event should go as follows:
+ * - lock cq/qp_table and look up struct
+ * - increment ref count in struct
+ * - drop cq/qp_table lock
+ * - lock struct, do your thing, and unlock struct
+ * - decrement ref count; if zero, wake up waiters
+ *
+ * To destroy a CQ/QP, we can do the following:
+ * - lock cq/qp_table, remove pointer, unlock cq/qp_table lock
+ * - decrement ref count
+ * - wait_event until ref count is zero
+ *
+ * It is the consumer's responsibilty to make sure that no QP
+ * operations (WQE posting or state modification) are pending when the
+ * QP is destroyed.  Also, the consumer must make sure that calls to
+ * qp_modify are serialized.
+ *
+ * Possible optimizations (wait for profile data to see if/where we
+ * have locks bouncing between CPUs):
+ * - split cq/qp table lock into n separate (cache-aligned) locks,
+ *   indexed (say) by the page in the table
+ */
+
+struct ipath_cq {
+       struct ib_cq ibcq;
+       struct tasklet_struct comptask;
+       spinlock_t lock;
+       u8 notify;
+       u8 triggered;
+       u32 head;               /* new records added to the head */
+       u32 tail;               /* poll_cq() reads from here. */
+       struct ib_wc *queue;    /* this is actually ibcq.cqe + 1 */
+};
+
+/*
+ * Send work request queue entry.
+ * The size of the sg_list is determined when the QP is created and stored
+ * in qp->s_max_sge.
+ */
+struct ipath_swqe {
+       struct ib_send_wr wr;   /* don't use wr.sg_list */
+       u32 psn;                /* first packet sequence number */
+       u32 lpsn;               /* last packet sequence number */
+       u32 ssn;                /* send sequence number */
+       u32 length;             /* total length of data in sg_list */
+       struct ipath_sge sg_list[0];
+};
+
+/*
+ * Receive work request queue entry.
+ * The size of the sg_list is determined when the QP is created and stored
+ * in qp->r_max_sge.
+ */
+struct ipath_rwqe {
+       u64 wr_id;
+       u32 length;             /* total length of data in sg_list */
+       u8 num_sge;
+       struct ipath_sge sg_list[0];
+};
+
+struct ipath_rq {
+       spinlock_t lock;
+       u32 head;               /* new work requests posted to the head */
+       u32 tail;               /* receives pull requests from here. */
+       u32 size;               /* size of RWQE array */
+       u8 max_sge;
+       struct ipath_rwqe *wq;  /* RWQE array */
+};
+
+struct ipath_srq {
+       struct ib_srq ibsrq;
+       struct ipath_rq rq;
+       /* send signal when number of RWQEs < limit */
+       u32 limit;
+};
+
+/*
+ * Variables prefixed with s_ are for the requester (sender).
+ * Variables prefixed with r_ are for the responder (receiver).
+ * Variables prefixed with ack_ are for responder replies.
+ *
+ * Common variables are protected by both r_rq.lock and s_lock in that order
+ * which only happens in modify_qp() or changing the QP 'state'.
+ */
+struct ipath_qp {
+       struct ib_qp ibqp;
+       struct ipath_qp *next;  /* link list for QPN hash table */
+       struct list_head piowait;       /* link for wait PIO buf */
+       struct list_head timerwait;     /* link for waiting for timeouts */
+       struct ib_ah_attr remote_ah_attr;
+       struct ipath_ib_header s_hdr;   /* next packet header to send */
+       atomic_t refcount;
+       wait_queue_head_t wait;
+       struct tasklet_struct s_task;
+       struct ipath_sge_state *s_cur_sge;
+       struct ipath_sge_state s_sge;   /* current send request data */
+       /* current RDMA read send data */
+       struct ipath_sge_state s_rdma_sge;
+       struct ipath_sge_state r_sge;   /* current receive data */
+       spinlock_t s_lock;
+       unsigned long s_flags;
+       u32 s_hdrwords;         /* size of s_hdr in 32 bit words */
+       u32 s_cur_size;         /* size of send packet in bytes */
+       u32 s_len;              /* total length of s_sge */
+       u32 s_rdma_len;         /* total length of s_rdma_sge */
+       u32 s_next_psn;         /* PSN for next request */
+       u32 s_last_psn;         /* last response PSN processed */
+       u32 s_psn;              /* current packet sequence number */
+       u32 s_rnr_timeout;      /* number of milliseconds for RNR timeout */
+       u32 s_ack_psn;          /* PSN for next ACK or RDMA_READ */
+       u64 s_ack_atomic;       /* data for atomic ACK */
+       u64 r_wr_id;            /* ID for current receive WQE */
+       u64 r_atomic_data;      /* data for last atomic op */
+       u32 r_atomic_psn;       /* PSN of last atomic op */
+       u32 r_len;              /* total length of r_sge */
+       u32 r_rcv_len;          /* receive data len processed */
+       u32 r_psn;              /* expected rcv packet sequence number */
+       u8 state;               /* QP state */
+       u8 s_state;             /* opcode of last packet sent */
+       u8 s_ack_state;         /* opcode of packet to ACK */
+       u8 s_nak_state;         /* non-zero if NAK is pending */
+       u8 r_state;             /* opcode of last packet received */
+       u8 r_reuse_sge;         /* for UC receive errors */
+       u8 r_sge_inx;           /* current index into sg_list */
+       u8 s_max_sge;           /* size of s_wq->sg_list */
+       u8 qp_access_flags;
+       u8 s_retry_cnt;         /* number of times to retry */
+       u8 s_rnr_retry_cnt;
+       u8 s_min_rnr_timer;
+       u8 s_retry;             /* requester retry counter */
+       u8 s_rnr_retry;         /* requester RNR retry counter */
+       u8 s_pkey_index;        /* PKEY index to use */
+       enum ib_mtu path_mtu;
+       atomic_t msn;           /* message sequence number */
+       u32 remote_qpn;
+       u32 qkey;               /* QKEY for this QP (for UD or RD) */
+       u32 s_size;             /* send work queue size */
+       u32 s_head;             /* new entries added here */
+       u32 s_tail;             /* next entry to process */
+       u32 s_cur;              /* current work queue entry */
+       u32 s_last;             /* last un-ACK'ed entry */
+       u32 s_ssn;              /* SSN of tail entry */
+       u32 s_lsn;              /* limit sequence number (credit) */
+       struct ipath_swqe *s_wq;        /* send work queue */
+       struct ipath_rq r_rq;   /* receive work queue */
+};
+
+/*
+ * Bit definitions for s_flags.
+ */
+#define IPATH_S_BUSY           0
+#define IPATH_S_SIGNAL_REQ_WR  1
+
+/*
+ * Since struct ipath_swqe is not a fixed size, we can't simply index into
+ * struct ipath_qp.s_wq.  This function does the array index computation.
+ */
+static inline struct ipath_swqe *get_swqe_ptr(struct ipath_qp *qp,
+                                             unsigned n)
+{
+       return (struct ipath_swqe *)((char *)qp->s_wq +
+                                    (sizeof(struct ipath_swqe) +
+                                     qp->s_max_sge *
+                                     sizeof(struct ipath_sge)) * n);
+}
+
+/*
+ * Since struct ipath_rwqe is not a fixed size, we can't simply index into
+ * struct ipath_rq.wq.  This function does the array index computation.
+ */
+static inline struct ipath_rwqe *get_rwqe_ptr(struct ipath_rq *rq,
+                                             unsigned n)
+{
+       return (struct ipath_rwqe *)
+               ((char *) rq->wq +
+                (sizeof(struct ipath_rwqe) +
+                 rq->max_sge * sizeof(struct ipath_sge)) * n);
+}
+
+/*
+ * QPN-map pages start out as NULL, they get allocated upon
+ * first use and are never deallocated. This way,
+ * large bitmaps are not allocated unless large numbers of QPs are used.
+ */
+struct qpn_map {
+       atomic_t n_free;
+       void *page;
+};
+
+struct ipath_qp_table {
+       spinlock_t lock;
+       u32 last;               /* last QP number allocated */
+       u32 max;                /* size of the hash table */
+       u32 nmaps;              /* size of the map table */
+       struct ipath_qp **table;
+       /* bit map of free numbers */
+       struct qpn_map map[QPNMAP_ENTRIES];
+};
+
+struct ipath_lkey_table {
+       spinlock_t lock;
+       u32 next;               /* next unused index (speeds search) */
+       u32 gen;                /* generation count */
+       u32 max;                /* size of the table */
+       struct ipath_mregion **table;
+};
+
+struct ipath_opcode_stats {
+       u64 n_packets;          /* number of packets */
+       u64 n_bytes;            /* total number of bytes */
+};
+
+struct ipath_ibdev {
+       struct ib_device ibdev;
+       struct list_head dev_list;
+       struct ipath_devdata *dd;
+       int ib_unit;            /* This is the device number */
+       u16 sm_lid;             /* in host order */
+       u8 sm_sl;
+       u8 mkeyprot_resv_lmc;
+       /* non-zero when timer is set */
+       unsigned long mkey_lease_timeout;
+
+       /* The following fields are really per port. */
+       struct ipath_qp_table qp_table;
+       struct ipath_lkey_table lk_table;
+       struct list_head pending[3];    /* FIFO of QPs waiting for ACKs */
+       struct list_head piowait;       /* list for wait PIO buf */
+       /* list of QPs waiting for RNR timer */
+       struct list_head rnrwait;
+       spinlock_t pending_lock;
+       __be64 sys_image_guid;  /* in network order */
+       __be64 gid_prefix;      /* in network order */
+       __be64 mkey;
+       u64 ipath_sword;        /* total dwords sent (sample result) */
+       u64 ipath_rword;        /* total dwords received (sample result) */
+       u64 ipath_spkts;        /* total packets sent (sample result) */
+       u64 ipath_rpkts;        /* total packets received (sample result) */
+       /* # of ticks no data sent (sample result) */
+       u64 ipath_xmit_wait;
+       u64 rcv_errors;         /* # of packets with SW detected rcv errs */
+       u64 n_unicast_xmit;     /* total unicast packets sent */
+       u64 n_unicast_rcv;      /* total unicast packets received */
+       u64 n_multicast_xmit;   /* total multicast packets sent */
+       u64 n_multicast_rcv;    /* total multicast packets received */
+       u64 n_symbol_error_counter;     /* starting count for PMA */
+       u64 n_link_error_recovery_counter;      /* starting count for PMA */
+       u64 n_link_downed_counter;      /* starting count for PMA */
+       u64 n_port_rcv_errors;  /* starting count for PMA */
+       u64 n_port_rcv_remphys_errors;  /* starting count for PMA */
+       u64 n_port_xmit_discards;       /* starting count for PMA */
+       u64 n_port_xmit_data;   /* starting count for PMA */
+       u64 n_port_rcv_data;    /* starting count for PMA */
+       u64 n_port_xmit_packets;        /* starting count for PMA */
+       u64 n_port_rcv_packets; /* starting count for PMA */
+       u32 n_pkey_violations;  /* starting count for PMA */
+       u32 n_rc_resends;
+       u32 n_rc_acks;
+       u32 n_rc_qacks;
+       u32 n_seq_naks;
+       u32 n_rdma_seq;
+       u32 n_rnr_naks;
+       u32 n_other_naks;
+       u32 n_timeouts;
+       u32 n_pkt_drops;
+       u32 n_wqe_errs;
+       u32 n_rdma_dup_busy;
+       u32 n_piowait;
+       u32 n_no_piobuf;
+       u32 port_cap_flags;
+       u32 pma_sample_start;
+       u32 pma_sample_interval;
+       __be16 pma_counter_select[5];
+       u16 pma_tag;
+       u16 qkey_violations;
+       u16 mkey_violations;
+       u16 mkey_lease_period;
+       u16 pending_index;      /* which pending queue is active */
+       u8 pma_sample_status;
+       u8 subnet_timeout;
+       u8 link_width_enabled;
+       u8 vl_high_limit;
+       struct ipath_opcode_stats opstats[128];
+};
+
+struct ipath_ucontext {
+       struct ib_ucontext ibucontext;
+};
+
+static inline struct ipath_mr *to_imr(struct ib_mr *ibmr)
+{
+       return container_of(ibmr, struct ipath_mr, ibmr);
+}
+
+static inline struct ipath_fmr *to_ifmr(struct ib_fmr *ibfmr)
+{
+       return container_of(ibfmr, struct ipath_fmr, ibfmr);
+}
+
+static inline struct ipath_pd *to_ipd(struct ib_pd *ibpd)
+{
+       return container_of(ibpd, struct ipath_pd, ibpd);
+}
+
+static inline struct ipath_ah *to_iah(struct ib_ah *ibah)
+{
+       return container_of(ibah, struct ipath_ah, ibah);
+}
+
+static inline struct ipath_cq *to_icq(struct ib_cq *ibcq)
+{
+       return container_of(ibcq, struct ipath_cq, ibcq);
+}
+
+static inline struct ipath_srq *to_isrq(struct ib_srq *ibsrq)
+{
+       return container_of(ibsrq, struct ipath_srq, ibsrq);
+}
+
+static inline struct ipath_qp *to_iqp(struct ib_qp *ibqp)
+{
+       return container_of(ibqp, struct ipath_qp, ibqp);
+}
+
+static inline struct ipath_ibdev *to_idev(struct ib_device *ibdev)
+{
+       return container_of(ibdev, struct ipath_ibdev, ibdev);
+}
+
+int ipath_process_mad(struct ib_device *ibdev,
+                     int mad_flags,
+                     u8 port_num,
+                     struct ib_wc *in_wc,
+                     struct ib_grh *in_grh,
+                     struct ib_mad *in_mad, struct ib_mad *out_mad);
+
+static inline struct ipath_ucontext *to_iucontext(struct ib_ucontext
+                                                 *ibucontext)
+{
+       return container_of(ibucontext, struct ipath_ucontext, ibucontext);
+}
+
+/*
+ * Compare the lower 24 bits of the two values.
+ * Returns an integer <, ==, or > than zero.
+ */
+static inline int ipath_cmp24(u32 a, u32 b)
+{
+       return (((int) a) - ((int) b)) << 8;
+}
+
+struct ipath_mcast *ipath_mcast_find(union ib_gid *mgid);
+
+int ipath_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid);
+
+int ipath_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid);
+
+int ipath_mcast_tree_empty(void);
+
+__be32 ipath_compute_aeth(struct ipath_qp *qp);
+
+struct ipath_qp *ipath_lookup_qpn(struct ipath_qp_table *qpt, u32 qpn);
+
+struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
+                             struct ib_qp_init_attr *init_attr,
+                             struct ib_udata *udata);
+
+int ipath_destroy_qp(struct ib_qp *ibqp);
+
+int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+                   int attr_mask);
+
+int ipath_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+                  int attr_mask, struct ib_qp_init_attr *init_attr);
+
+void ipath_free_all_qps(struct ipath_qp_table *qpt);
+
+int ipath_init_qp_table(struct ipath_ibdev *idev, int size);
+
+void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc);
+
+void ipath_error_qp(struct ipath_qp *qp);
+
+void ipath_get_credit(struct ipath_qp *qp, u32 aeth);
+
+void ipath_do_rc_send(unsigned long data);
+
+void ipath_do_uc_send(unsigned long data);
+
+void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig);
+
+int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
+                 u32 len, u64 vaddr, u32 rkey, int acc);
+
+int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
+                 struct ib_sge *sge, int acc);
+
+void ipath_copy_sge(struct ipath_sge_state *ss, void *data, u32 length);
+
+void ipath_skip_sge(struct ipath_sge_state *ss, u32 length);
+
+int ipath_post_rc_send(struct ipath_qp *qp, struct ib_send_wr *wr);
+
+void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
+                 int has_grh, void *data, u32 tlen, struct ipath_qp *qp);
+
+void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
+                 int has_grh, void *data, u32 tlen, struct ipath_qp *qp);
+
+void ipath_restart_rc(struct ipath_qp *qp, u32 psn, struct ib_wc *wc);
+
+void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_sge_state *ss,
+                      u32 length, struct ib_send_wr *wr, struct ib_wc *wc);
+
+int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr);
+
+void ipath_ud_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
+                 int has_grh, void *data, u32 tlen, struct ipath_qp *qp);
+
+int ipath_alloc_lkey(struct ipath_lkey_table *rkt,
+                    struct ipath_mregion *mr);
+
+void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey);
+
+int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
+                 struct ib_sge *sge, int acc);
+
+int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
+                 u32 len, u64 vaddr, u32 rkey, int acc);
+
+int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr,
+                          struct ib_recv_wr **bad_wr);
+
+struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
+                               struct ib_srq_init_attr *srq_init_attr,
+                               struct ib_udata *udata);
+
+int ipath_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
+                    enum ib_srq_attr_mask attr_mask);
+
+int ipath_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr);
+
+int ipath_destroy_srq(struct ib_srq *ibsrq);
+
+void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig);
+
+int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
+
+struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries,
+                             struct ib_ucontext *context,
+                             struct ib_udata *udata);
+
+int ipath_destroy_cq(struct ib_cq *ibcq);
+
+int ipath_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify notify);
+
+int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata);
+
+struct ib_mr *ipath_get_dma_mr(struct ib_pd *pd, int acc);
+
+struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd,
+                               struct ib_phys_buf *buffer_list,
+                               int num_phys_buf, int acc, u64 *iova_start);
+
+struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region,
+                               int mr_access_flags,
+                               struct ib_udata *udata);
+
+int ipath_dereg_mr(struct ib_mr *ibmr);
+
+struct ib_fmr *ipath_alloc_fmr(struct ib_pd *pd, int mr_access_flags,
+                              struct ib_fmr_attr *fmr_attr);
+
+int ipath_map_phys_fmr(struct ib_fmr *ibfmr, u64 * page_list,
+                      int list_len, u64 iova);
+
+int ipath_unmap_fmr(struct list_head *fmr_list);
+
+int ipath_dealloc_fmr(struct ib_fmr *ibfmr);
+
+void ipath_no_bufs_available(struct ipath_qp *qp, struct ipath_ibdev *dev);
+
+void ipath_insert_rnr_queue(struct ipath_qp *qp);
+
+int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only);
+
+void ipath_ruc_loopback(struct ipath_qp *sqp, struct ib_wc *wc);
+
+extern const enum ib_wc_opcode ib_ipath_wc_opcode[];
+
+extern const u8 ipath_cvt_physportstate[];
+
+extern const int ib_ipath_state_ops[];
+
+extern unsigned int ib_ipath_lkey_table_size;
+
+extern const u32 ib_ipath_rnr_table[];
+
+#endif                         /* IPATH_VERBS_H */
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c
new file mode 100644 (file)
index 0000000..10b31d2
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+
+#include "ipath_verbs.h"
+
+/*
+ * Global table of GID to attached QPs.
+ * The table is global to all ipath devices since a send from one QP/device
+ * needs to be locally routed to any locally attached QPs on the same
+ * or different device.
+ */
+static struct rb_root mcast_tree;
+static DEFINE_SPINLOCK(mcast_lock);
+
+/**
+ * ipath_mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct
+ * @qp: the QP to link
+ */
+static struct ipath_mcast_qp *ipath_mcast_qp_alloc(struct ipath_qp *qp)
+{
+       struct ipath_mcast_qp *mqp;
+
+       mqp = kmalloc(sizeof *mqp, GFP_KERNEL);
+       if (!mqp)
+               goto bail;
+
+       mqp->qp = qp;
+       atomic_inc(&qp->refcount);
+
+bail:
+       return mqp;
+}
+
+static void ipath_mcast_qp_free(struct ipath_mcast_qp *mqp)
+{
+       struct ipath_qp *qp = mqp->qp;
+
+       /* Notify ipath_destroy_qp() if it is waiting. */
+       if (atomic_dec_and_test(&qp->refcount))
+               wake_up(&qp->wait);
+
+       kfree(mqp);
+}
+
+/**
+ * ipath_mcast_alloc - allocate the multicast GID structure
+ * @mgid: the multicast GID
+ *
+ * A list of QPs will be attached to this structure.
+ */
+static struct ipath_mcast *ipath_mcast_alloc(union ib_gid *mgid)
+{
+       struct ipath_mcast *mcast;
+
+       mcast = kmalloc(sizeof *mcast, GFP_KERNEL);
+       if (!mcast)
+               goto bail;
+
+       mcast->mgid = *mgid;
+       INIT_LIST_HEAD(&mcast->qp_list);
+       init_waitqueue_head(&mcast->wait);
+       atomic_set(&mcast->refcount, 0);
+
+bail:
+       return mcast;
+}
+
+static void ipath_mcast_free(struct ipath_mcast *mcast)
+{
+       struct ipath_mcast_qp *p, *tmp;
+
+       list_for_each_entry_safe(p, tmp, &mcast->qp_list, list)
+               ipath_mcast_qp_free(p);
+
+       kfree(mcast);
+}
+
+/**
+ * ipath_mcast_find - search the global table for the given multicast GID
+ * @mgid: the multicast GID to search for
+ *
+ * Returns NULL if not found.
+ *
+ * The caller is responsible for decrementing the reference count if found.
+ */
+struct ipath_mcast *ipath_mcast_find(union ib_gid *mgid)
+{
+       struct rb_node *n;
+       unsigned long flags;
+       struct ipath_mcast *mcast;
+
+       spin_lock_irqsave(&mcast_lock, flags);
+       n = mcast_tree.rb_node;
+       while (n) {
+               int ret;
+
+               mcast = rb_entry(n, struct ipath_mcast, rb_node);
+
+               ret = memcmp(mgid->raw, mcast->mgid.raw,
+                            sizeof(union ib_gid));
+               if (ret < 0)
+                       n = n->rb_left;
+               else if (ret > 0)
+                       n = n->rb_right;
+               else {
+                       atomic_inc(&mcast->refcount);
+                       spin_unlock_irqrestore(&mcast_lock, flags);
+                       goto bail;
+               }
+       }
+       spin_unlock_irqrestore(&mcast_lock, flags);
+
+       mcast = NULL;
+
+bail:
+       return mcast;
+}
+
+/**
+ * ipath_mcast_add - insert mcast GID into table and attach QP struct
+ * @mcast: the mcast GID table
+ * @mqp: the QP to attach
+ *
+ * Return zero if both were added.  Return EEXIST if the GID was already in
+ * the table but the QP was added.  Return ESRCH if the QP was already
+ * attached and neither structure was added.
+ */
+static int ipath_mcast_add(struct ipath_mcast *mcast,
+                          struct ipath_mcast_qp *mqp)
+{
+       struct rb_node **n = &mcast_tree.rb_node;
+       struct rb_node *pn = NULL;
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&mcast_lock, flags);
+
+       while (*n) {
+               struct ipath_mcast *tmcast;
+               struct ipath_mcast_qp *p;
+
+               pn = *n;
+               tmcast = rb_entry(pn, struct ipath_mcast, rb_node);
+
+               ret = memcmp(mcast->mgid.raw, tmcast->mgid.raw,
+                            sizeof(union ib_gid));
+               if (ret < 0) {
+                       n = &pn->rb_left;
+                       continue;
+               }
+               if (ret > 0) {
+                       n = &pn->rb_right;
+                       continue;
+               }
+
+               /* Search the QP list to see if this is already there. */
+               list_for_each_entry_rcu(p, &tmcast->qp_list, list) {
+                       if (p->qp == mqp->qp) {
+                               spin_unlock_irqrestore(&mcast_lock, flags);
+                               ret = ESRCH;
+                               goto bail;
+                       }
+               }
+               list_add_tail_rcu(&mqp->list, &tmcast->qp_list);
+               spin_unlock_irqrestore(&mcast_lock, flags);
+               ret = EEXIST;
+               goto bail;
+       }
+
+       list_add_tail_rcu(&mqp->list, &mcast->qp_list);
+
+       atomic_inc(&mcast->refcount);
+       rb_link_node(&mcast->rb_node, pn, n);
+       rb_insert_color(&mcast->rb_node, &mcast_tree);
+
+       spin_unlock_irqrestore(&mcast_lock, flags);
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+int ipath_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+       struct ipath_qp *qp = to_iqp(ibqp);
+       struct ipath_mcast *mcast;
+       struct ipath_mcast_qp *mqp;
+       int ret;
+
+       /*
+        * Allocate data structures since its better to do this outside of
+        * spin locks and it will most likely be needed.
+        */
+       mcast = ipath_mcast_alloc(gid);
+       if (mcast == NULL) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+       mqp = ipath_mcast_qp_alloc(qp);
+       if (mqp == NULL) {
+               ipath_mcast_free(mcast);
+               ret = -ENOMEM;
+               goto bail;
+       }
+       switch (ipath_mcast_add(mcast, mqp)) {
+       case ESRCH:
+               /* Neither was used: can't attach the same QP twice. */
+               ipath_mcast_qp_free(mqp);
+               ipath_mcast_free(mcast);
+               ret = -EINVAL;
+               goto bail;
+       case EEXIST:            /* The mcast wasn't used */
+               ipath_mcast_free(mcast);
+               break;
+       default:
+               break;
+       }
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+int ipath_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+       struct ipath_qp *qp = to_iqp(ibqp);
+       struct ipath_mcast *mcast = NULL;
+       struct ipath_mcast_qp *p, *tmp;
+       struct rb_node *n;
+       unsigned long flags;
+       int last = 0;
+       int ret;
+
+       spin_lock_irqsave(&mcast_lock, flags);
+
+       /* Find the GID in the mcast table. */
+       n = mcast_tree.rb_node;
+       while (1) {
+               if (n == NULL) {
+                       spin_unlock_irqrestore(&mcast_lock, flags);
+                       ret = 0;
+                       goto bail;
+               }
+
+               mcast = rb_entry(n, struct ipath_mcast, rb_node);
+               ret = memcmp(gid->raw, mcast->mgid.raw,
+                            sizeof(union ib_gid));
+               if (ret < 0)
+                       n = n->rb_left;
+               else if (ret > 0)
+                       n = n->rb_right;
+               else
+                       break;
+       }
+
+       /* Search the QP list. */
+       list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) {
+               if (p->qp != qp)
+                       continue;
+               /*
+                * We found it, so remove it, but don't poison the forward
+                * link until we are sure there are no list walkers.
+                */
+               list_del_rcu(&p->list);
+
+               /* If this was the last attached QP, remove the GID too. */
+               if (list_empty(&mcast->qp_list)) {
+                       rb_erase(&mcast->rb_node, &mcast_tree);
+                       last = 1;
+               }
+               break;
+       }
+
+       spin_unlock_irqrestore(&mcast_lock, flags);
+
+       if (p) {
+               /*
+                * Wait for any list walkers to finish before freeing the
+                * list element.
+                */
+               wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1);
+               ipath_mcast_qp_free(p);
+       }
+       if (last) {
+               atomic_dec(&mcast->refcount);
+               wait_event(mcast->wait, !atomic_read(&mcast->refcount));
+               ipath_mcast_free(mcast);
+       }
+
+       ret = 0;
+
+bail:
+       return ret;
+}
+
+int ipath_mcast_tree_empty(void)
+{
+       return mcast_tree.rb_node == NULL;
+}
diff --git a/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
new file mode 100644 (file)
index 0000000..adc5322
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * This file is conditionally built on x86_64 only.  Otherwise weak symbol
+ * versions of the functions exported from here are used.
+ */
+
+#include <linux/pci.h>
+#include <asm/mtrr.h>
+#include <asm/processor.h>
+
+#include "ipath_kernel.h"
+
+/**
+ * ipath_enable_wc - enable write combining for MMIO writes to the device
+ * @dd: infinipath device
+ *
+ * This routine is x86_64-specific; it twiddles the CPU's MTRRs to enable
+ * write combining.
+ */
+int ipath_enable_wc(struct ipath_devdata *dd)
+{
+       int ret = 0;
+       u64 pioaddr, piolen;
+       unsigned bits;
+       const unsigned long addr = pci_resource_start(dd->pcidev, 0);
+       const size_t len = pci_resource_len(dd->pcidev, 0);
+
+       /*
+        * Set the PIO buffers to be WCCOMB, so we get HT bursts to the
+        * chip.  Linux (possibly the hardware) requires it to be on a power
+        * of 2 address matching the length (which has to be a power of 2).
+        * For rev1, that means the base address, for rev2, it will be just
+        * the PIO buffers themselves.
+        */
+       pioaddr = addr + dd->ipath_piobufbase;
+       piolen = (dd->ipath_piobcnt2k +
+                 dd->ipath_piobcnt4k) *
+               ALIGN(dd->ipath_piobcnt2k +
+                     dd->ipath_piobcnt4k, dd->ipath_palign);
+
+       for (bits = 0; !(piolen & (1ULL << bits)); bits++)
+               /* do nothing */ ;
+
+       if (piolen != (1ULL << bits)) {
+               piolen >>= bits;
+               while (piolen >>= 1)
+                       bits++;
+               piolen = 1ULL << (bits + 1);
+       }
+       if (pioaddr & (piolen - 1)) {
+               u64 atmp;
+               ipath_dbg("pioaddr %llx not on right boundary for size "
+                         "%llx, fixing\n",
+                         (unsigned long long) pioaddr,
+                         (unsigned long long) piolen);
+               atmp = pioaddr & ~(piolen - 1);
+               if (atmp < addr || (atmp + piolen) > (addr + len)) {
+                       ipath_dev_err(dd, "No way to align address/size "
+                                     "(%llx/%llx), no WC mtrr\n",
+                                     (unsigned long long) atmp,
+                                     (unsigned long long) piolen << 1);
+                       ret = -ENODEV;
+               } else {
+                       ipath_dbg("changing WC base from %llx to %llx, "
+                                 "len from %llx to %llx\n",
+                                 (unsigned long long) pioaddr,
+                                 (unsigned long long) atmp,
+                                 (unsigned long long) piolen,
+                                 (unsigned long long) piolen << 1);
+                       pioaddr = atmp;
+                       piolen <<= 1;
+               }
+       }
+
+       if (!ret) {
+               int cookie;
+               ipath_cdbg(VERBOSE, "Setting mtrr for chip to WC "
+                          "(addr %llx, len=0x%llx)\n",
+                          (unsigned long long) pioaddr,
+                          (unsigned long long) piolen);
+               cookie = mtrr_add(pioaddr, piolen, MTRR_TYPE_WRCOMB, 0);
+               if (cookie < 0) {
+                       {
+                               dev_info(&dd->pcidev->dev,
+                                        "mtrr_add()  WC for PIO bufs "
+                                        "failed (%d)\n",
+                                        cookie);
+                               ret = -EINVAL;
+                       }
+               } else {
+                       ipath_cdbg(VERBOSE, "Set mtrr for chip to WC, "
+                                  "cookie is %d\n", cookie);
+                       dd->ipath_wc_cookie = cookie;
+               }
+       }
+
+       return ret;
+}
+
+/**
+ * ipath_disable_wc - disable write combining for MMIO writes to the device
+ * @dd: infinipath device
+ */
+void ipath_disable_wc(struct ipath_devdata *dd)
+{
+       if (dd->ipath_wc_cookie) {
+               ipath_cdbg(VERBOSE, "undoing WCCOMB on pio buffers\n");
+               mtrr_del(dd->ipath_wc_cookie, 0, 0);
+               dd->ipath_wc_cookie = 0;
+       }
+}
+
+/**
+ * ipath_unordered_wc - indicate whether write combining is ordered
+ *
+ * Because our performance depends on our ability to do write combining mmio
+ * writes in the most efficient way, we need to know if we are on an Intel
+ * or AMD x86_64 processor.  AMD x86_64 processors flush WC buffers out in
+ * the order completed, and so no special flushing is required to get
+ * correct ordering.  Intel processors, however, will flush write buffers
+ * out in "random" orders, and so explicit ordering is needed at times.
+ */
+int ipath_unordered_wc(void)
+{
+       return boot_cpu_data.x86_vendor != X86_VENDOR_AMD;
+}
diff --git a/drivers/infiniband/hw/ipath/ips_common.h b/drivers/infiniband/hw/ipath/ips_common.h
new file mode 100644 (file)
index 0000000..410a764
--- /dev/null
@@ -0,0 +1,263 @@
+#ifndef IPS_COMMON_H
+#define IPS_COMMON_H
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ipath_common.h"
+
+struct ipath_header {
+       /*
+        * Version - 4 bits, Port - 4 bits, TID - 10 bits and Offset -
+        * 14 bits before ECO change ~28 Dec 03.  After that, Vers 4,
+        * Port 3, TID 11, offset 14.
+        */
+       __le32 ver_port_tid_offset;
+       __le16 chksum;
+       __le16 pkt_flags;
+};
+
+struct ips_message_header {
+       __be16 lrh[4];
+       __be32 bth[3];
+       /* fields below this point are in host byte order */
+       struct ipath_header iph;
+       __u8 sub_opcode;
+       __u8 flags;
+       __u16 src_rank;
+       /* 24 bits. The upper 8 bit is available for other use */
+       union {
+               struct {
+                       unsigned ack_seq_num:24;
+                       unsigned port:4;
+                       unsigned unused:4;
+               };
+               __u32 ack_seq_num_org;
+       };
+       __u8 expected_tid_session_id;
+       __u8 tinylen;           /* to aid MPI */
+       union {
+           __u16 tag;          /* to aid MPI */
+           __u16 mqhdr;        /* for PSM MQ */
+       };
+       union {
+               __u32 mpi[4];   /* to aid MPI */
+               __u32 data[4];
+               __u64 mq[2];    /* for PSM MQ */
+               struct {
+                       __u16 mtu;
+                       __u8 major_ver;
+                       __u8 minor_ver;
+                       __u32 not_used; //free
+                       __u32 run_id;
+                       __u32 client_ver;
+               };
+       };
+};
+
+struct ether_header {
+       __be16 lrh[4];
+       __be32 bth[3];
+       struct ipath_header iph;
+       __u8 sub_opcode;
+       __u8 cmd;
+       __be16 lid;
+       __u16 mac[3];
+       __u8 frag_num;
+       __u8 seq_num;
+       __le32 len;
+       /* MUST be of word size due to PIO write requirements */
+       __u32 csum;
+       __le16 csum_offset;
+       __le16 flags;
+       __u16 first_2_bytes;
+       __u8 unused[2];         /* currently unused */
+};
+
+/*
+ * The PIO buffer used for sending infinipath messages must only be written
+ * in 32-bit words, all the data must be written, and no writes can occur
+ * after the last word is written (which transfers "ownership" of the buffer
+ * to the chip and triggers the message to be sent).
+ * Since the Linux sk_buff structure can be recursive, non-aligned, and
+ * any number of bytes in each segment, we use the following structure
+ * to keep information about the overall state of the copy operation.
+ * This is used to save the information needed to store the checksum
+ * in the right place before sending the last word to the hardware and
+ * to buffer the last 0-3 bytes of non-word sized segments.
+ */
+struct copy_data_s {
+       struct ether_header *hdr;
+       /* addr of PIO buf to write csum to */
+       __u32 __iomem *csum_pio;
+       __u32 __iomem *to;      /* addr of PIO buf to write data to */
+       __u32 device;           /* which device to allocate PIO bufs from */
+       __s32 error;            /* set if there is an error. */
+       __s32 extra;            /* amount of data saved in u.buf below */
+       __u32 len;              /* total length to send in bytes */
+       __u32 flen;             /* frament length in words */
+       __u32 csum;             /* partial IP checksum */
+       __u32 pos;              /* position for partial checksum */
+       __u32 offset;           /* offset to where data currently starts */
+       __s32 checksum_calc;    /* set to 1 when csum has been calculated */
+       struct sk_buff *skb;
+       union {
+               __u32 w;
+               __u8 buf[4];
+       } u;
+};
+
+/* IB - LRH header consts */
+#define IPS_LRH_GRH 0x0003     /* 1. word of IB LRH - next header: GRH */
+#define IPS_LRH_BTH 0x0002     /* 1. word of IB LRH - next header: BTH */
+
+#define IPS_OFFSET  0
+
+/*
+ * defines the cut-off point between the header queue and eager/expected
+ * TID queue
+ */
+#define NUM_OF_EXTRA_WORDS_IN_HEADER_QUEUE \
+       ((sizeof(struct ips_message_header) - \
+         offsetof(struct ips_message_header, iph)) >> 2)
+
+/* OpCodes  */
+#define OPCODE_IPS 0xC0
+#define OPCODE_ITH4X 0xC1
+
+/* OpCode 30 is use by stand-alone test programs  */
+#define OPCODE_RAW_DATA 0xDE
+/* last OpCode (31) is reserved for test  */
+#define OPCODE_TEST 0xDF
+
+/* sub OpCodes - ips  */
+#define OPCODE_SEQ_DATA 0x01
+#define OPCODE_SEQ_CTRL 0x02
+
+#define OPCODE_SEQ_MQ_DATA 0x03
+#define OPCODE_SEQ_MQ_CTRL 0x04
+
+#define OPCODE_ACK 0x10
+#define OPCODE_NAK 0x11
+
+#define OPCODE_ERR_CHK 0x20
+#define OPCODE_ERR_CHK_PLS 0x21
+
+#define OPCODE_STARTUP 0x30
+#define OPCODE_STARTUP_ACK 0x31
+#define OPCODE_STARTUP_NAK 0x32
+
+#define OPCODE_STARTUP_EXT 0x34
+#define OPCODE_STARTUP_ACK_EXT 0x35
+#define OPCODE_STARTUP_NAK_EXT 0x36
+
+#define OPCODE_TIDS_RELEASE 0x40
+#define OPCODE_TIDS_RELEASE_CONFIRM 0x41
+
+#define OPCODE_CLOSE 0x50
+#define OPCODE_CLOSE_ACK 0x51
+/*
+ * like OPCODE_CLOSE, but no complaint if other side has already closed.
+ * Used when doing abort(), MPI_Abort(), etc.
+ */
+#define OPCODE_ABORT 0x52
+
+/* sub OpCodes - ith4x  */
+#define OPCODE_ENCAP 0x81
+#define OPCODE_LID_ARP 0x82
+
+/* Receive Header Queue: receive type (from infinipath) */
+#define RCVHQ_RCV_TYPE_EXPECTED  0
+#define RCVHQ_RCV_TYPE_EAGER     1
+#define RCVHQ_RCV_TYPE_NON_KD    2
+#define RCVHQ_RCV_TYPE_ERROR     3
+
+/* misc. */
+#define SIZE_OF_CRC 1
+
+#define EAGER_TID_ID INFINIPATH_I_TID_MASK
+
+#define IPS_DEFAULT_P_KEY 0xFFFF
+
+#define IPS_PERMISSIVE_LID 0xFFFF
+#define IPS_MULTICAST_LID_BASE 0xC000
+
+#define IPS_AETH_CREDIT_SHIFT 24
+#define IPS_AETH_CREDIT_MASK 0x1F
+#define IPS_AETH_CREDIT_INVAL 0x1F
+
+#define IPS_PSN_MASK 0xFFFFFF
+#define IPS_MSN_MASK 0xFFFFFF
+#define IPS_QPN_MASK 0xFFFFFF
+#define IPS_MULTICAST_QPN 0xFFFFFF
+
+/* functions for extracting fields from rcvhdrq entries */
+static inline __u32 ips_get_hdr_err_flags(const __le32 * rbuf)
+{
+       return __le32_to_cpu(rbuf[1]);
+}
+
+static inline __u32 ips_get_index(const __le32 * rbuf)
+{
+       return (__le32_to_cpu(rbuf[0]) >> INFINIPATH_RHF_EGRINDEX_SHIFT)
+           & INFINIPATH_RHF_EGRINDEX_MASK;
+}
+
+static inline __u32 ips_get_rcv_type(const __le32 * rbuf)
+{
+       return (__le32_to_cpu(rbuf[0]) >> INFINIPATH_RHF_RCVTYPE_SHIFT)
+           & INFINIPATH_RHF_RCVTYPE_MASK;
+}
+
+static inline __u32 ips_get_length_in_bytes(const __le32 * rbuf)
+{
+       return ((__le32_to_cpu(rbuf[0]) >> INFINIPATH_RHF_LENGTH_SHIFT)
+               & INFINIPATH_RHF_LENGTH_MASK) << 2;
+}
+
+static inline void *ips_get_first_protocol_header(const __u32 * rbuf)
+{
+       return (void *)&rbuf[2];
+}
+
+static inline struct ips_message_header *ips_get_ips_header(const __u32 *
+                                                           rbuf)
+{
+       return (struct ips_message_header *)&rbuf[2];
+}
+
+static inline __u32 ips_get_ipath_ver(__le32 hdrword)
+{
+       return (__le32_to_cpu(hdrword) >> INFINIPATH_I_VERS_SHIFT)
+           & INFINIPATH_I_VERS_MASK;
+}
+
+#endif                         /* IPS_COMMON_H */
diff --git a/drivers/infiniband/hw/ipath/verbs_debug.h b/drivers/infiniband/hw/ipath/verbs_debug.h
new file mode 100644 (file)
index 0000000..40d693c
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VERBS_DEBUG_H
+#define _VERBS_DEBUG_H
+
+/*
+ * This file contains tracing code for the ib_ipath kernel module.
+ */
+#ifndef _VERBS_DEBUGGING       /* tracing enabled or not */
+#define _VERBS_DEBUGGING 1
+#endif
+
+extern unsigned ib_ipath_debug;
+
+#define _VERBS_ERROR(fmt,...) \
+       do { \
+               printk(KERN_ERR "%s: " fmt, "ib_ipath", ##__VA_ARGS__); \
+       } while(0)
+
+#define _VERBS_UNIT_ERROR(unit,fmt,...) \
+       do { \
+               printk(KERN_ERR "%s: " fmt, "ib_ipath", ##__VA_ARGS__); \
+       } while(0)
+
+#if _VERBS_DEBUGGING
+
+/*
+ * Mask values for debugging.  The scheme allows us to compile out any
+ * of the debug tracing stuff, and if compiled in, to enable or
+ * disable dynamically.
+ * This can be set at modprobe time also:
+ *      modprobe ib_path ib_ipath_debug=3
+ */
+
+#define __VERBS_INFO        0x1        /* generic low verbosity stuff */
+#define __VERBS_DBG         0x2        /* generic debug */
+#define __VERBS_VDBG        0x4        /* verbose debug */
+#define __VERBS_SMADBG      0x8000     /* sma packet debug */
+
+#define _VERBS_INFO(fmt,...) \
+       do { \
+               if (unlikely(ib_ipath_debug&__VERBS_INFO)) \
+                       printk(KERN_INFO "%s: " fmt,"ib_ipath", \
+                              ##__VA_ARGS__); \
+       } while(0)
+
+#define _VERBS_DBG(fmt,...) \
+       do { \
+               if (unlikely(ib_ipath_debug&__VERBS_DBG)) \
+                       printk(KERN_DEBUG "%s: " fmt, __func__, \
+                              ##__VA_ARGS__); \
+       } while(0)
+
+#define _VERBS_VDBG(fmt,...) \
+       do { \
+               if (unlikely(ib_ipath_debug&__VERBS_VDBG)) \
+                       printk(KERN_DEBUG "%s: " fmt, __func__, \
+                              ##__VA_ARGS__); \
+       } while(0)
+
+#define _VERBS_SMADBG(fmt,...) \
+       do { \
+               if (unlikely(ib_ipath_debug&__VERBS_SMADBG)) \
+                       printk(KERN_DEBUG "%s: " fmt, __func__, \
+                              ##__VA_ARGS__); \
+       } while(0)
+
+#else /* ! _VERBS_DEBUGGING */
+
+#define _VERBS_INFO(fmt,...)
+#define _VERBS_DBG(fmt,...)
+#define _VERBS_VDBG(fmt,...)
+#define _VERBS_SMADBG(fmt,...)
+
+#endif /* _VERBS_DEBUGGING */
+
+#endif /* _VERBS_DEBUG_H */
index d782893..07358fb 100644 (file)
@@ -49,9 +49,8 @@ static struct input_handle *evbug_connect(struct input_handler *handler, struct
 {
        struct input_handle *handle;
 
-       if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL)))
+       if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL)))
                return NULL;
-       memset(handle, 0, sizeof(struct input_handle));
 
        handle->dev = dev;
        handle->handler = handler;
index 745979f..a34e3d9 100644 (file)
@@ -130,9 +130,8 @@ static int evdev_open(struct inode * inode, struct file * file)
        if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file)))
                return accept_err;
 
-       if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL)))
+       if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL)))
                return -ENOMEM;
-       memset(list, 0, sizeof(struct evdev_list));
 
        list->evdev = evdev_table[i];
        list_add_tail(&list->node, &evdev_table[i]->list);
@@ -609,9 +608,8 @@ static struct input_handle *evdev_connect(struct input_handler *handler, struct
                return NULL;
        }
 
-       if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL)))
+       if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)))
                return NULL;
-       memset(evdev, 0, sizeof(struct evdev));
 
        INIT_LIST_HEAD(&evdev->list);
        init_waitqueue_head(&evdev->wait);
index b765a15..36644bf 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/delay.h>
 #include <linux/kthread.h>
 #include <linux/sched.h>       /* HZ */
+#include <linux/mutex.h>
 
 /*#include <asm/io.h>*/
 
@@ -43,10 +44,10 @@ EXPORT_SYMBOL(gameport_start_polling);
 EXPORT_SYMBOL(gameport_stop_polling);
 
 /*
- * gameport_sem protects entire gameport subsystem and is taken
+ * gameport_mutex protects entire gameport subsystem and is taken
  * every time gameport port or driver registrered or unregistered.
  */
-static DECLARE_MUTEX(gameport_sem);
+static DEFINE_MUTEX(gameport_mutex);
 
 static LIST_HEAD(gameport_list);
 
@@ -265,6 +266,7 @@ static void gameport_queue_event(void *object, struct module *owner,
        if ((event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC))) {
                if (!try_module_get(owner)) {
                        printk(KERN_WARNING "gameport: Can't get module reference, dropping event %d\n", event_type);
+                       kfree(event);
                        goto out;
                }
 
@@ -342,7 +344,7 @@ static void gameport_handle_event(void)
        struct gameport_event *event;
        struct gameport_driver *gameport_drv;
 
-       down(&gameport_sem);
+       mutex_lock(&gameport_mutex);
 
        /*
         * Note that we handle only one event here to give swsusp
@@ -379,7 +381,7 @@ static void gameport_handle_event(void)
                gameport_free_event(event);
        }
 
-       up(&gameport_sem);
+       mutex_unlock(&gameport_mutex);
 }
 
 /*
@@ -464,7 +466,7 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut
        struct device_driver *drv;
        int retval;
 
-       retval = down_interruptible(&gameport_sem);
+       retval = mutex_lock_interruptible(&gameport_mutex);
        if (retval)
                return retval;
 
@@ -484,7 +486,7 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut
                retval = -EINVAL;
        }
 
-       up(&gameport_sem);
+       mutex_unlock(&gameport_mutex);
 
        return retval;
 }
@@ -521,7 +523,7 @@ static void gameport_init_port(struct gameport *gameport)
 
        __module_get(THIS_MODULE);
 
-       init_MUTEX(&gameport->drv_sem);
+       mutex_init(&gameport->drv_mutex);
        device_initialize(&gameport->dev);
        snprintf(gameport->dev.bus_id, sizeof(gameport->dev.bus_id),
                 "gameport%lu", (unsigned long)atomic_inc_return(&gameport_no) - 1);
@@ -661,10 +663,10 @@ void __gameport_register_port(struct gameport *gameport, struct module *owner)
  */
 void gameport_unregister_port(struct gameport *gameport)
 {
-       down(&gameport_sem);
+       mutex_lock(&gameport_mutex);
        gameport_disconnect_port(gameport);
        gameport_destroy_port(gameport);
-       up(&gameport_sem);
+       mutex_unlock(&gameport_mutex);
 }
 
 
@@ -717,7 +719,7 @@ void gameport_unregister_driver(struct gameport_driver *drv)
 {
        struct gameport *gameport;
 
-       down(&gameport_sem);
+       mutex_lock(&gameport_mutex);
        drv->ignore = 1;        /* so gameport_find_driver ignores it */
 
 start_over:
@@ -731,7 +733,7 @@ start_over:
        }
 
        driver_unregister(&drv->driver);
-       up(&gameport_sem);
+       mutex_unlock(&gameport_mutex);
 }
 
 static int gameport_bus_match(struct device *dev, struct device_driver *drv)
@@ -743,9 +745,9 @@ static int gameport_bus_match(struct device *dev, struct device_driver *drv)
 
 static void gameport_set_drv(struct gameport *gameport, struct gameport_driver *drv)
 {
-       down(&gameport->drv_sem);
+       mutex_lock(&gameport->drv_mutex);
        gameport->drv = drv;
-       up(&gameport->drv_sem);
+       mutex_unlock(&gameport->drv_mutex);
 }
 
 int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode)
@@ -796,5 +798,5 @@ static void __exit gameport_exit(void)
        kthread_stop(gameport_task);
 }
 
-module_init(gameport_init);
+subsys_initcall(gameport_init);
 module_exit(gameport_exit);
index d2e55dc..3e2d28f 100644 (file)
@@ -252,14 +252,14 @@ static struct pnp_driver ns558_pnp_driver;
 
 #endif
 
-static int pnp_registered = 0;
-
 static int __init ns558_init(void)
 {
        int i = 0;
+       int error;
 
-       if (pnp_register_driver(&ns558_pnp_driver) >= 0)
-               pnp_registered = 1;
+       error = pnp_register_driver(&ns558_pnp_driver);
+       if (error && error != -ENODEV)  /* should be ENOSYS really */
+               return error;
 
 /*
  * Probe ISA ports after PnP, so that PnP ports that are already
@@ -270,7 +270,7 @@ static int __init ns558_init(void)
        while (ns558_isa_portlist[i])
                ns558_isa_probe(ns558_isa_portlist[i++]);
 
-       return (list_empty(&ns558_list) && !pnp_registered) ? -ENODEV : 0;
+       return list_empty(&ns558_list) && error ? -ENODEV : 0;
 }
 
 static void __exit ns558_exit(void)
@@ -283,8 +283,7 @@ static void __exit ns558_exit(void)
                kfree(ns558);
        }
 
-       if (pnp_registered)
-               pnp_unregister_driver(&ns558_pnp_driver);
+       pnp_unregister_driver(&ns558_pnp_driver);
 }
 
 module_init(ns558_init);
index f8af094..a935abe 100644 (file)
 #include <linux/random.h>
 #include <linux/major.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <linux/interrupt.h>
 #include <linux/poll.h>
 #include <linux/device.h>
+#include <linux/mutex.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_DESCRIPTION("Input core");
@@ -224,7 +226,7 @@ int input_open_device(struct input_handle *handle)
        struct input_dev *dev = handle->dev;
        int err;
 
-       err = down_interruptible(&dev->sem);
+       err = mutex_lock_interruptible(&dev->mutex);
        if (err)
                return err;
 
@@ -236,7 +238,7 @@ int input_open_device(struct input_handle *handle)
        if (err)
                handle->open--;
 
-       up(&dev->sem);
+       mutex_unlock(&dev->mutex);
 
        return err;
 }
@@ -255,13 +257,13 @@ void input_close_device(struct input_handle *handle)
 
        input_release_device(handle);
 
-       down(&dev->sem);
+       mutex_lock(&dev->mutex);
 
        if (!--dev->users && dev->close)
                dev->close(dev);
        handle->open--;
 
-       up(&dev->sem);
+       mutex_unlock(&dev->mutex);
 }
 
 static void input_link_handle(struct input_handle *handle)
@@ -315,21 +317,6 @@ static struct input_device_id *input_match_device(struct input_device_id *id, st
        return NULL;
 }
 
-static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, int max)
-{
-       int i;
-       int len = 0;
-
-       for (i = NBITS(max) - 1; i > 0; i--)
-               if (bitmap[i])
-                       break;
-
-       for (; i >= 0; i--)
-               len += snprintf(buf + len, max(buf_size - len, 0),
-                               "%lx%s", bitmap[i], i > 0 ? " " : "");
-       return len;
-}
-
 #ifdef CONFIG_PROC_FS
 
 static struct proc_dir_entry *proc_bus_input_dir;
@@ -342,7 +329,7 @@ static inline void input_wakeup_procfs_readers(void)
        wake_up(&input_devices_poll_wait);
 }
 
-static unsigned int input_devices_poll(struct file *file, poll_table *wait)
+static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
 {
        int state = input_devices_state;
        poll_wait(file, &input_devices_poll_wait, wait);
@@ -351,115 +338,171 @@ static unsigned int input_devices_poll(struct file *file, poll_table *wait)
        return 0;
 }
 
-#define SPRINTF_BIT(ev, bm)                                            \
-       do {                                                            \
-               len += sprintf(buf + len, "B: %s=", #ev);               \
-               len += input_print_bitmap(buf + len, INT_MAX,           \
-                                       dev->bm##bit, ev##_MAX);        \
-               len += sprintf(buf + len, "\n");                        \
-       } while (0)
+static struct list_head *list_get_nth_element(struct list_head *list, loff_t *pos)
+{
+       struct list_head *node;
+       loff_t i = 0;
 
-#define TEST_AND_SPRINTF_BIT(ev, bm)                                   \
-       do {                                                            \
-               if (test_bit(EV_##ev, dev->evbit))                      \
-                       SPRINTF_BIT(ev, bm);                            \
-       } while (0)
+       list_for_each(node, list)
+               if (i++ == *pos)
+                       return node;
+
+       return NULL;
+}
 
-static int input_devices_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
+static struct list_head *list_get_next_element(struct list_head *list, struct list_head *element, loff_t *pos)
 {
-       struct input_dev *dev;
-       struct input_handle *handle;
-       const char *path;
+       if (element->next == list)
+               return NULL;
+
+       ++(*pos);
+       return element->next;
+}
+
+static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       /* acquire lock here ... Yes, we do need locking, I knowi, I know... */
+
+       return list_get_nth_element(&input_dev_list, pos);
+}
 
-       off_t at = 0;
-       int len, cnt = 0;
+static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       return list_get_next_element(&input_dev_list, v, pos);
+}
 
-       list_for_each_entry(dev, &input_dev_list, node) {
+static void input_devices_seq_stop(struct seq_file *seq, void *v)
+{
+       /* release lock here */
+}
 
-               path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
+static void input_seq_print_bitmap(struct seq_file *seq, const char *name,
+                                  unsigned long *bitmap, int max)
+{
+       int i;
 
-               len = sprintf(buf, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",
-                       dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);
+       for (i = NBITS(max) - 1; i > 0; i--)
+               if (bitmap[i])
+                       break;
 
-               len += sprintf(buf + len, "N: Name=\"%s\"\n", dev->name ? dev->name : "");
-               len += sprintf(buf + len, "P: Phys=%s\n", dev->phys ? dev->phys : "");
-               len += sprintf(buf + len, "S: Sysfs=%s\n", path ? path : "");
-               len += sprintf(buf + len, "H: Handlers=");
+       seq_printf(seq, "B: %s=", name);
+       for (; i >= 0; i--)
+               seq_printf(seq, "%lx%s", bitmap[i], i > 0 ? " " : "");
+       seq_putc(seq, '\n');
+}
 
-               list_for_each_entry(handle, &dev->h_list, d_node)
-                       len += sprintf(buf + len, "%s ", handle->name);
-
-               len += sprintf(buf + len, "\n");
-
-               SPRINTF_BIT(EV, ev);
-               TEST_AND_SPRINTF_BIT(KEY, key);
-               TEST_AND_SPRINTF_BIT(REL, rel);
-               TEST_AND_SPRINTF_BIT(ABS, abs);
-               TEST_AND_SPRINTF_BIT(MSC, msc);
-               TEST_AND_SPRINTF_BIT(LED, led);
-               TEST_AND_SPRINTF_BIT(SND, snd);
-               TEST_AND_SPRINTF_BIT(FF, ff);
-               TEST_AND_SPRINTF_BIT(SW, sw);
-
-               len += sprintf(buf + len, "\n");
-
-               at += len;
-
-               if (at >= pos) {
-                       if (!*start) {
-                               *start = buf + (pos - (at - len));
-                               cnt = at - pos;
-                       } else  cnt += len;
-                       buf += len;
-                       if (cnt >= count)
-                               break;
-               }
+static int input_devices_seq_show(struct seq_file *seq, void *v)
+{
+       struct input_dev *dev = container_of(v, struct input_dev, node);
+       const char *path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
+       struct input_handle *handle;
 
-               kfree(path);
-       }
+       seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",
+                  dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);
 
-       if (&dev->node == &input_dev_list)
-               *eof = 1;
+       seq_printf(seq, "N: Name=\"%s\"\n", dev->name ? dev->name : "");
+       seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : "");
+       seq_printf(seq, "S: Sysfs=%s\n", path ? path : "");
+       seq_printf(seq, "H: Handlers=");
 
-       return (count > cnt) ? cnt : count;
+       list_for_each_entry(handle, &dev->h_list, d_node)
+               seq_printf(seq, "%s ", handle->name);
+       seq_putc(seq, '\n');
+
+       input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX);
+       if (test_bit(EV_KEY, dev->evbit))
+               input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX);
+       if (test_bit(EV_REL, dev->evbit))
+               input_seq_print_bitmap(seq, "REL", dev->relbit, REL_MAX);
+       if (test_bit(EV_ABS, dev->evbit))
+               input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX);
+       if (test_bit(EV_MSC, dev->evbit))
+               input_seq_print_bitmap(seq, "MSC", dev->mscbit, MSC_MAX);
+       if (test_bit(EV_LED, dev->evbit))
+               input_seq_print_bitmap(seq, "LED", dev->ledbit, LED_MAX);
+       if (test_bit(EV_SND, dev->evbit))
+               input_seq_print_bitmap(seq, "SND", dev->sndbit, SND_MAX);
+       if (test_bit(EV_FF, dev->evbit))
+               input_seq_print_bitmap(seq, "FF", dev->ffbit, FF_MAX);
+       if (test_bit(EV_SW, dev->evbit))
+               input_seq_print_bitmap(seq, "SW", dev->swbit, SW_MAX);
+
+       seq_putc(seq, '\n');
+
+       kfree(path);
+       return 0;
 }
 
-static int input_handlers_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
+static struct seq_operations input_devices_seq_ops = {
+       .start  = input_devices_seq_start,
+       .next   = input_devices_seq_next,
+       .stop   = input_devices_seq_stop,
+       .show   = input_devices_seq_show,
+};
+
+static int input_proc_devices_open(struct inode *inode, struct file *file)
 {
-       struct input_handler *handler;
+       return seq_open(file, &input_devices_seq_ops);
+}
 
-       off_t at = 0;
-       int len = 0, cnt = 0;
-       int i = 0;
+static struct file_operations input_devices_fileops = {
+       .owner          = THIS_MODULE,
+       .open           = input_proc_devices_open,
+       .poll           = input_proc_devices_poll,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
 
-       list_for_each_entry(handler, &input_handler_list, node) {
+static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       /* acquire lock here ... Yes, we do need locking, I knowi, I know... */
+       seq->private = (void *)(unsigned long)*pos;
+       return list_get_nth_element(&input_handler_list, pos);
+}
+
+static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       seq->private = (void *)(unsigned long)(*pos + 1);
+       return list_get_next_element(&input_handler_list, v, pos);
+}
 
-               if (handler->fops)
-                       len = sprintf(buf, "N: Number=%d Name=%s Minor=%d\n",
-                               i++, handler->name, handler->minor);
-               else
-                       len = sprintf(buf, "N: Number=%d Name=%s\n",
-                               i++, handler->name);
+static void input_handlers_seq_stop(struct seq_file *seq, void *v)
+{
+       /* release lock here */
+}
 
-               at += len;
+static int input_handlers_seq_show(struct seq_file *seq, void *v)
+{
+       struct input_handler *handler = container_of(v, struct input_handler, node);
 
-               if (at >= pos) {
-                       if (!*start) {
-                               *start = buf + (pos - (at - len));
-                               cnt = at - pos;
-                       } else  cnt += len;
-                       buf += len;
-                       if (cnt >= count)
-                               break;
-               }
-       }
-       if (&handler->node == &input_handler_list)
-               *eof = 1;
+       seq_printf(seq, "N: Number=%ld Name=%s",
+                  (unsigned long)seq->private, handler->name);
+       if (handler->fops)
+               seq_printf(seq, " Minor=%d", handler->minor);
+       seq_putc(seq, '\n');
 
-       return (count > cnt) ? cnt : count;
+       return 0;
 }
+static struct seq_operations input_handlers_seq_ops = {
+       .start  = input_handlers_seq_start,
+       .next   = input_handlers_seq_next,
+       .stop   = input_handlers_seq_stop,
+       .show   = input_handlers_seq_show,
+};
 
-static struct file_operations input_fileops;
+static int input_proc_handlers_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &input_handlers_seq_ops);
+}
+
+static struct file_operations input_handlers_fileops = {
+       .owner          = THIS_MODULE,
+       .open           = input_proc_handlers_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
 
 static int __init input_proc_init(void)
 {
@@ -471,20 +514,19 @@ static int __init input_proc_init(void)
 
        proc_bus_input_dir->owner = THIS_MODULE;
 
-       entry = create_proc_read_entry("devices", 0, proc_bus_input_dir, input_devices_read, NULL);
+       entry = create_proc_entry("devices", 0, proc_bus_input_dir);
        if (!entry)
                goto fail1;
 
        entry->owner = THIS_MODULE;
-       input_fileops = *entry->proc_fops;
-       input_fileops.poll = input_devices_poll;
-       entry->proc_fops = &input_fileops;
+       entry->proc_fops = &input_devices_fileops;
 
-       entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL);
+       entry = create_proc_entry("handlers", 0, proc_bus_input_dir);
        if (!entry)
                goto fail2;
 
        entry->owner = THIS_MODULE;
+       entry->proc_fops = &input_handlers_fileops;
 
        return 0;
 
@@ -512,13 +554,14 @@ static ssize_t input_dev_show_##name(struct class_device *dev, char *buf) \
        struct input_dev *input_dev = to_input_dev(dev);                        \
        int retval;                                                             \
                                                                                \
-       retval = down_interruptible(&input_dev->sem);                           \
+       retval = mutex_lock_interruptible(&input_dev->mutex);                   \
        if (retval)                                                             \
                return retval;                                                  \
                                                                                \
-       retval = sprintf(buf, "%s\n", input_dev->name ? input_dev->name : "");  \
+       retval = scnprintf(buf, PAGE_SIZE,                                      \
+                          "%s\n", input_dev->name ? input_dev->name : "");     \
                                                                                \
-       up(&input_dev->sem);                                                    \
+       mutex_unlock(&input_dev->mutex);                                        \
                                                                                \
        return retval;                                                          \
 }                                                                              \
@@ -528,46 +571,51 @@ INPUT_DEV_STRING_ATTR_SHOW(name);
 INPUT_DEV_STRING_ATTR_SHOW(phys);
 INPUT_DEV_STRING_ATTR_SHOW(uniq);
 
-static int print_modalias_bits(char *buf, int size, char prefix, unsigned long *arr,
-                              unsigned int min, unsigned int max)
+static int input_print_modalias_bits(char *buf, int size,
+                                    char name, unsigned long *bm,
+                                    unsigned int min_bit, unsigned int max_bit)
 {
-       int len, i;
+       int len = 0, i;
 
-       len = snprintf(buf, size, "%c", prefix);
-       for (i = min; i < max; i++)
-               if (arr[LONG(i)] & BIT(i))
-                       len += snprintf(buf + len, size - len, "%X,", i);
+       len += snprintf(buf, max(size, 0), "%c", name);
+       for (i = min_bit; i < max_bit; i++)
+               if (bm[LONG(i)] & BIT(i))
+                       len += snprintf(buf + len, max(size - len, 0), "%X,", i);
        return len;
 }
 
-static int print_modalias(char *buf, int size, struct input_dev *id)
+static int input_print_modalias(char *buf, int size, struct input_dev *id,
+                               int add_cr)
 {
        int len;
 
-       len = snprintf(buf, size, "input:b%04Xv%04Xp%04Xe%04X-",
-                      id->id.bustype,
-                      id->id.vendor,
-                      id->id.product,
-                      id->id.version);
-
-       len += print_modalias_bits(buf + len, size - len, 'e', id->evbit,
-                                  0, EV_MAX);
-       len += print_modalias_bits(buf + len, size - len, 'k', id->keybit,
-                                  KEY_MIN_INTERESTING, KEY_MAX);
-       len += print_modalias_bits(buf + len, size - len, 'r', id->relbit,
-                                  0, REL_MAX);
-       len += print_modalias_bits(buf + len, size - len, 'a', id->absbit,
-                                  0, ABS_MAX);
-       len += print_modalias_bits(buf + len, size - len, 'm', id->mscbit,
-                                  0, MSC_MAX);
-       len += print_modalias_bits(buf + len, size - len, 'l', id->ledbit,
-                                  0, LED_MAX);
-       len += print_modalias_bits(buf + len, size - len, 's', id->sndbit,
-                                  0, SND_MAX);
-       len += print_modalias_bits(buf + len, size - len, 'f', id->ffbit,
-                                  0, FF_MAX);
-       len += print_modalias_bits(buf + len, size - len, 'w', id->swbit,
-                                  0, SW_MAX);
+       len = snprintf(buf, max(size, 0),
+                      "input:b%04Xv%04Xp%04Xe%04X-",
+                      id->id.bustype, id->id.vendor,
+                      id->id.product, id->id.version);
+
+       len += input_print_modalias_bits(buf + len, size - len,
+                               'e', id->evbit, 0, EV_MAX);
+       len += input_print_modalias_bits(buf + len, size - len,
+                               'k', id->keybit, KEY_MIN_INTERESTING, KEY_MAX);
+       len += input_print_modalias_bits(buf + len, size - len,
+                               'r', id->relbit, 0, REL_MAX);
+       len += input_print_modalias_bits(buf + len, size - len,
+                               'a', id->absbit, 0, ABS_MAX);
+       len += input_print_modalias_bits(buf + len, size - len,
+                               'm', id->mscbit, 0, MSC_MAX);
+       len += input_print_modalias_bits(buf + len, size - len,
+                               'l', id->ledbit, 0, LED_MAX);
+       len += input_print_modalias_bits(buf + len, size - len,
+                               's', id->sndbit, 0, SND_MAX);
+       len += input_print_modalias_bits(buf + len, size - len,
+                               'f', id->ffbit, 0, FF_MAX);
+       len += input_print_modalias_bits(buf + len, size - len,
+                               'w', id->swbit, 0, SW_MAX);
+
+       if (add_cr)
+               len += snprintf(buf + len, max(size - len, 0), "\n");
+
        return len;
 }
 
@@ -576,9 +624,9 @@ static ssize_t input_dev_show_modalias(struct class_device *dev, char *buf)
        struct input_dev *id = to_input_dev(dev);
        ssize_t len;
 
-       len = print_modalias(buf, PAGE_SIZE, id);
-       len += snprintf(buf + len, PAGE_SIZE-len, "\n");
-       return len;
+       len = input_print_modalias(buf, PAGE_SIZE, id, 1);
+
+       return max_t(int, len, PAGE_SIZE);
 }
 static CLASS_DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL);
 
@@ -598,7 +646,7 @@ static struct attribute_group input_dev_attr_group = {
 static ssize_t input_dev_show_id_##name(struct class_device *dev, char *buf)   \
 {                                                                              \
        struct input_dev *input_dev = to_input_dev(dev);                        \
-       return sprintf(buf, "%04x\n", input_dev->id.name);                      \
+       return scnprintf(buf, PAGE_SIZE, "%04x\n", input_dev->id.name);         \
 }                                                                              \
 static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL);
 
@@ -620,11 +668,33 @@ static struct attribute_group input_dev_id_attr_group = {
        .attrs  = input_dev_id_attrs,
 };
 
+static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap,
+                             int max, int add_cr)
+{
+       int i;
+       int len = 0;
+
+       for (i = NBITS(max) - 1; i > 0; i--)
+               if (bitmap[i])
+                       break;
+
+       for (; i >= 0; i--)
+               len += snprintf(buf + len, max(buf_size - len, 0),
+                               "%lx%s", bitmap[i], i > 0 ? " " : "");
+
+       if (add_cr)
+               len += snprintf(buf + len, max(buf_size - len, 0), "\n");
+
+       return len;
+}
+
 #define INPUT_DEV_CAP_ATTR(ev, bm)                                             \
 static ssize_t input_dev_show_cap_##bm(struct class_device *dev, char *buf)    \
 {                                                                              \
        struct input_dev *input_dev = to_input_dev(dev);                        \
-       return input_print_bitmap(buf, PAGE_SIZE, input_dev->bm##bit, ev##_MAX);\
+       int len = input_print_bitmap(buf, PAGE_SIZE,                            \
+                                    input_dev->bm##bit, ev##_MAX, 1);          \
+       return min_t(int, len, PAGE_SIZE);                                      \
 }                                                                              \
 static CLASS_DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL);
 
@@ -669,8 +739,8 @@ static void input_dev_release(struct class_device *class_dev)
  * device bitfields.
  */
 static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index,
-                                   char *buffer, int buffer_size, int *cur_len,
-                                   const char *name, unsigned long *bitmap, int max)
+                                  char *buffer, int buffer_size, int *cur_len,
+                                  const char *name, unsigned long *bitmap, int max)
 {
        if (*cur_index >= num_envp - 1)
                return -ENOMEM;
@@ -678,12 +748,36 @@ static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index,
        envp[*cur_index] = buffer + *cur_len;
 
        *cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0), name);
-       if (*cur_len > buffer_size)
+       if (*cur_len >= buffer_size)
                return -ENOMEM;
 
        *cur_len += input_print_bitmap(buffer + *cur_len,
                                        max(buffer_size - *cur_len, 0),
-                                       bitmap, max) + 1;
+                                       bitmap, max, 0) + 1;
+       if (*cur_len > buffer_size)
+               return -ENOMEM;
+
+       (*cur_index)++;
+       return 0;
+}
+
+static int input_add_uevent_modalias_var(char **envp, int num_envp, int *cur_index,
+                                        char *buffer, int buffer_size, int *cur_len,
+                                        struct input_dev *dev)
+{
+       if (*cur_index >= num_envp - 1)
+               return -ENOMEM;
+
+       envp[*cur_index] = buffer + *cur_len;
+
+       *cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0),
+                            "MODALIAS=");
+       if (*cur_len >= buffer_size)
+               return -ENOMEM;
+
+       *cur_len += input_print_modalias(buffer + *cur_len,
+                                        max(buffer_size - *cur_len, 0),
+                                        dev, 0) + 1;
        if (*cur_len > buffer_size)
                return -ENOMEM;
 
@@ -693,7 +787,7 @@ static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index,
 
 #define INPUT_ADD_HOTPLUG_VAR(fmt, val...)                             \
        do {                                                            \
-               int err = add_uevent_var(envp, num_envp, &i,    \
+               int err = add_uevent_var(envp, num_envp, &i,            \
                                        buffer, buffer_size, &len,      \
                                        fmt, val);                      \
                if (err)                                                \
@@ -709,6 +803,16 @@ static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index,
                        return err;                                     \
        } while (0)
 
+#define INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev)                            \
+       do {                                                            \
+               int err = input_add_uevent_modalias_var(envp,           \
+                                       num_envp, &i,                   \
+                                       buffer, buffer_size, &len,      \
+                                       dev);                           \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
 static int input_dev_uevent(struct class_device *cdev, char **envp,
                            int num_envp, char *buffer, int buffer_size)
 {
@@ -744,9 +848,7 @@ static int input_dev_uevent(struct class_device *cdev, char **envp,
        if (test_bit(EV_SW, dev->evbit))
                INPUT_ADD_HOTPLUG_BM_VAR("SW=", dev->swbit, SW_MAX);
 
-       envp[i++] = buffer + len;
-       len += snprintf(buffer + len, buffer_size - len, "MODALIAS=");
-       len += print_modalias(buffer + len, buffer_size - len, dev) + 1;
+       INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev);
 
        envp[i] = NULL;
        return 0;
@@ -790,7 +892,7 @@ int input_register_device(struct input_dev *dev)
                return -EINVAL;
        }
 
-       init_MUTEX(&dev->sem);
+       mutex_init(&dev->mutex);
        set_bit(EV_SYN, dev->evbit);
 
        /*
index 20e2972..949bdce 100644 (file)
@@ -171,9 +171,8 @@ static int joydev_open(struct inode *inode, struct file *file)
        if (i >= JOYDEV_MINORS || !joydev_table[i])
                return -ENODEV;
 
-       if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL)))
+       if (!(list = kzalloc(sizeof(struct joydev_list), GFP_KERNEL)))
                return -ENOMEM;
-       memset(list, 0, sizeof(struct joydev_list));
 
        list->joydev = joydev_table[i];
        list_add_tail(&list->node, &joydev_table[i]->list);
@@ -457,9 +456,8 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct
                return NULL;
        }
 
-       if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL)))
+       if (!(joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL)))
                return NULL;
-       memset(joydev, 0, sizeof(struct joydev));
 
        INIT_LIST_HEAD(&joydev->list);
        init_waitqueue_head(&joydev->wait);
index ec55a29..7249d32 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
+#include <linux/mutex.h>
 
 #include <asm/system.h>
 #include <asm/amigahw.h>
@@ -52,7 +53,7 @@ MODULE_PARM_DESC(map, "Map of attached joysticks in form of <a>,<b> (default is
 __obsolete_setup("amijoy=");
 
 static int amijoy_used;
-static DECLARE_MUTEX(amijoy_sem);
+static DEFINE_MUTEX(amijoy_mutex);
 static struct input_dev *amijoy_dev[2];
 static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" };
 
@@ -85,7 +86,7 @@ static int amijoy_open(struct input_dev *dev)
 {
        int err;
 
-       err = down_interruptible(&amijoy_sem);
+       err = mutex_lock_interruptible(&amijoy_mutex);
        if (err)
                return err;
 
@@ -97,16 +98,16 @@ static int amijoy_open(struct input_dev *dev)
 
        amijoy_used++;
 out:
-       up(&amijoy_sem);
+       mutex_unlock(&amijoy_mutex);
        return err;
 }
 
 static void amijoy_close(struct input_dev *dev)
 {
-       down(&amijoy_sem);
+       mutex_lock(&amijoy_mutex);
        if (!--amijoy_used)
                free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt);
-       up(&amijoy_sem);
+       mutex_unlock(&amijoy_mutex);
 }
 
 static int __init amijoy_init(void)
index dcffc34..e618946 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/init.h>
 #include <linux/parport.h>
 #include <linux/input.h>
+#include <linux/mutex.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver");
@@ -111,7 +112,7 @@ struct db9 {
        struct pardevice *pd;
        int mode;
        int used;
-       struct semaphore sem;
+       struct mutex mutex;
        char phys[DB9_MAX_DEVICES][32];
 };
 
@@ -525,7 +526,7 @@ static int db9_open(struct input_dev *dev)
        struct parport *port = db9->pd->port;
        int err;
 
-       err = down_interruptible(&db9->sem);
+       err = mutex_lock_interruptible(&db9->mutex);
        if (err)
                return err;
 
@@ -539,7 +540,7 @@ static int db9_open(struct input_dev *dev)
                mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
        }
 
-       up(&db9->sem);
+       mutex_unlock(&db9->mutex);
        return 0;
 }
 
@@ -548,14 +549,14 @@ static void db9_close(struct input_dev *dev)
        struct db9 *db9 = dev->private;
        struct parport *port = db9->pd->port;
 
-       down(&db9->sem);
+       mutex_lock(&db9->mutex);
        if (!--db9->used) {
                del_timer_sync(&db9->timer);
                parport_write_control(port, 0x00);
                parport_data_forward(port);
                parport_release(db9->pd);
        }
-       up(&db9->sem);
+       mutex_unlock(&db9->mutex);
 }
 
 static struct db9 __init *db9_probe(int parport, int mode)
@@ -603,7 +604,7 @@ static struct db9 __init *db9_probe(int parport, int mode)
                goto err_unreg_pardev;
        }
 
-       init_MUTEX(&db9->sem);
+       mutex_init(&db9->mutex);
        db9->pd = pd;
        db9->mode = mode;
        init_timer(&db9->timer);
index 900587a..ecbdb6b 100644 (file)
@@ -7,6 +7,7 @@
  *  Based on the work of:
  *     Andree Borrmann         John Dahlstrom
  *     David Kuder             Nathan Hand
+ *     Raphael Assenat
  */
 
 /*
@@ -36,6 +37,7 @@
 #include <linux/init.h>
 #include <linux/parport.h>
 #include <linux/input.h>
+#include <linux/mutex.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver");
@@ -72,8 +74,9 @@ __obsolete_setup("gc_3=");
 #define GC_N64         6
 #define GC_PSX         7
 #define GC_DDR         8
+#define GC_SNESMOUSE   9
 
-#define GC_MAX         8
+#define GC_MAX         9
 
 #define GC_REFRESH_TIME        HZ/100
 
@@ -83,7 +86,7 @@ struct gc {
        struct timer_list timer;
        unsigned char pads[GC_MAX + 1];
        int used;
-       struct semaphore sem;
+       struct mutex mutex;
        char phys[GC_MAX_DEVICES][32];
 };
 
@@ -93,7 +96,7 @@ static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
 
 static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",
                                "Multisystem 2-button joystick", "N64 controller", "PSX controller",
-                               "PSX DDR controller" };
+                               "PSX DDR controller", "SNES mouse" };
 /*
  * N64 support.
  */
@@ -205,9 +208,12 @@ static void gc_n64_process_packet(struct gc *gc)
  * NES/SNES support.
  */
 
-#define GC_NES_DELAY   6       /* Delay between bits - 6us */
-#define GC_NES_LENGTH  8       /* The NES pads use 8 bits of data */
-#define GC_SNES_LENGTH 12      /* The SNES true length is 16, but the last 4 bits are unused */
+#define GC_NES_DELAY           6       /* Delay between bits - 6us */
+#define GC_NES_LENGTH          8       /* The NES pads use 8 bits of data */
+#define GC_SNES_LENGTH         12      /* The SNES true length is 16, but the
+                                          last 4 bits are unused */
+#define GC_SNESMOUSE_LENGTH    32      /* The SNES mouse uses 32 bits, the first
+                                          16 bits are equivalent to a gamepad */
 
 #define GC_NES_POWER   0xfc
 #define GC_NES_CLOCK   0x01
@@ -242,11 +248,15 @@ static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data)
 
 static void gc_nes_process_packet(struct gc *gc)
 {
-       unsigned char data[GC_SNES_LENGTH];
+       unsigned char data[GC_SNESMOUSE_LENGTH];
        struct input_dev *dev;
-       int i, j, s;
+       int i, j, s, len;
+       char x_rel, y_rel;
+
+       len = gc->pads[GC_SNESMOUSE] ? GC_SNESMOUSE_LENGTH :
+                       (gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH);
 
-       gc_nes_read_packet(gc, gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH, data);
+       gc_nes_read_packet(gc, len, data);
 
        for (i = 0; i < GC_MAX_DEVICES; i++) {
 
@@ -269,6 +279,44 @@ static void gc_nes_process_packet(struct gc *gc)
                        for (j = 0; j < 8; j++)
                                input_report_key(dev, gc_snes_btn[j], s & data[gc_snes_bytes[j]]);
 
+               if (s & gc->pads[GC_SNESMOUSE]) {
+                       /*
+                        * The 4 unused bits from SNES controllers appear to be ID bits
+                        * so use them to make sure iwe are dealing with a mouse.
+                        * gamepad is connected. This is important since
+                        * my SNES gamepad sends 1's for bits 16-31, which
+                        * cause the mouse pointer to quickly move to the
+                        * upper left corner of the screen.
+                        */
+                       if (!(s & data[12]) && !(s & data[13]) &&
+                           !(s & data[14]) && (s & data[15])) {
+                               input_report_key(dev, BTN_LEFT, s & data[9]);
+                               input_report_key(dev, BTN_RIGHT, s & data[8]);
+
+                               x_rel = y_rel = 0;
+                               for (j = 0; j < 7; j++) {
+                                       x_rel <<= 1;
+                                       if (data[25 + j] & s)
+                                               x_rel |= 1;
+
+                                       y_rel <<= 1;
+                                       if (data[17 + j] & s)
+                                               y_rel |= 1;
+                               }
+
+                               if (x_rel) {
+                                       if (data[24] & s)
+                                               x_rel = -x_rel;
+                                       input_report_rel(dev, REL_X, x_rel);
+                               }
+
+                               if (y_rel) {
+                                       if (data[16] & s)
+                                               y_rel = -y_rel;
+                                       input_report_rel(dev, REL_Y, y_rel);
+                               }
+                       }
+               }
                input_sync(dev);
        }
 }
@@ -524,10 +572,10 @@ static void gc_timer(unsigned long private)
                gc_n64_process_packet(gc);
 
 /*
- * NES and SNES pads
+ * NES and SNES pads or mouse
  */
 
-       if (gc->pads[GC_NES] || gc->pads[GC_SNES])
+       if (gc->pads[GC_NES] || gc->pads[GC_SNES] || gc->pads[GC_SNESMOUSE])
                gc_nes_process_packet(gc);
 
 /*
@@ -552,7 +600,7 @@ static int gc_open(struct input_dev *dev)
        struct gc *gc = dev->private;
        int err;
 
-       err = down_interruptible(&gc->sem);
+       err = mutex_lock_interruptible(&gc->mutex);
        if (err)
                return err;
 
@@ -562,7 +610,7 @@ static int gc_open(struct input_dev *dev)
                mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
        }
 
-       up(&gc->sem);
+       mutex_unlock(&gc->mutex);
        return 0;
 }
 
@@ -570,13 +618,13 @@ static void gc_close(struct input_dev *dev)
 {
        struct gc *gc = dev->private;
 
-       down(&gc->sem);
+       mutex_lock(&gc->mutex);
        if (!--gc->used) {
                del_timer_sync(&gc->timer);
                parport_write_control(gc->pd->port, 0x00);
                parport_release(gc->pd);
        }
-       up(&gc->sem);
+       mutex_unlock(&gc->mutex);
 }
 
 static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type)
@@ -609,10 +657,13 @@ static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type)
        input_dev->open = gc_open;
        input_dev->close = gc_close;
 
-       input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+       if (pad_type != GC_SNESMOUSE) {
+               input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
 
-       for (i = 0; i < 2; i++)
-               input_set_abs_params(input_dev, ABS_X + i, -1, 1, 0, 0);
+               for (i = 0; i < 2; i++)
+                       input_set_abs_params(input_dev, ABS_X + i, -1, 1, 0, 0);
+       } else
+               input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
 
        gc->pads[0] |= gc_status_bit[idx];
        gc->pads[pad_type] |= gc_status_bit[idx];
@@ -630,6 +681,13 @@ static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type)
 
                        break;
 
+               case GC_SNESMOUSE:
+                       set_bit(BTN_LEFT, input_dev->keybit);
+                       set_bit(BTN_RIGHT, input_dev->keybit);
+                       set_bit(REL_X, input_dev->relbit);
+                       set_bit(REL_Y, input_dev->relbit);
+                       break;
+
                case GC_SNES:
                        for (i = 4; i < 8; i++)
                                set_bit(gc_snes_btn[i], input_dev->keybit);
@@ -693,7 +751,7 @@ static struct gc __init *gc_probe(int parport, int *pads, int n_pads)
                goto err_unreg_pardev;
        }
 
-       init_MUTEX(&gc->sem);
+       mutex_init(&gc->mutex);
        gc->pd = pd;
        init_timer(&gc->timer);
        gc->timer.data = (long) gc;
index 4678b6d..2b8e845 100644 (file)
@@ -42,14 +42,14 @@ static int make_magnitude_modifier(struct iforce* iforce,
        unsigned char data[3];
 
        if (!no_alloc) {
-               down(&iforce->mem_mutex);
+               mutex_lock(&iforce->mem_mutex);
                if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
                        iforce->device_memory.start, iforce->device_memory.end, 2L,
                        NULL, NULL)) {
-                       up(&iforce->mem_mutex);
+                       mutex_unlock(&iforce->mem_mutex);
                        return -ENOMEM;
                }
-               up(&iforce->mem_mutex);
+               mutex_unlock(&iforce->mem_mutex);
        }
 
        data[0] = LO(mod_chunk->start);
@@ -75,14 +75,14 @@ static int make_period_modifier(struct iforce* iforce,
        period = TIME_SCALE(period);
 
        if (!no_alloc) {
-               down(&iforce->mem_mutex);
+               mutex_lock(&iforce->mem_mutex);
                if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
                        iforce->device_memory.start, iforce->device_memory.end, 2L,
                        NULL, NULL)) {
-                       up(&iforce->mem_mutex);
+                       mutex_unlock(&iforce->mem_mutex);
                        return -ENOMEM;
                }
-               up(&iforce->mem_mutex);
+               mutex_unlock(&iforce->mem_mutex);
        }
 
        data[0] = LO(mod_chunk->start);
@@ -115,14 +115,14 @@ static int make_envelope_modifier(struct iforce* iforce,
        fade_duration = TIME_SCALE(fade_duration);
 
        if (!no_alloc) {
-               down(&iforce->mem_mutex);
+               mutex_lock(&iforce->mem_mutex);
                if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
                        iforce->device_memory.start, iforce->device_memory.end, 2L,
                        NULL, NULL)) {
-                       up(&iforce->mem_mutex);
+                       mutex_unlock(&iforce->mem_mutex);
                        return -ENOMEM;
                }
-               up(&iforce->mem_mutex);
+               mutex_unlock(&iforce->mem_mutex);
        }
 
        data[0] = LO(mod_chunk->start);
@@ -152,14 +152,14 @@ static int make_condition_modifier(struct iforce* iforce,
        unsigned char data[10];
 
        if (!no_alloc) {
-               down(&iforce->mem_mutex);
+               mutex_lock(&iforce->mem_mutex);
                if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
                        iforce->device_memory.start, iforce->device_memory.end, 2L,
                        NULL, NULL)) {
-                       up(&iforce->mem_mutex);
+                       mutex_unlock(&iforce->mem_mutex);
                        return -ENOMEM;
                }
-               up(&iforce->mem_mutex);
+               mutex_unlock(&iforce->mem_mutex);
        }
 
        data[0] = LO(mod_chunk->start);
index b6bc049..ab0a26b 100644 (file)
@@ -350,7 +350,7 @@ int iforce_init_device(struct iforce *iforce)
 
        init_waitqueue_head(&iforce->wait);
        spin_lock_init(&iforce->xmit_lock);
-       init_MUTEX(&iforce->mem_mutex);
+       mutex_init(&iforce->mem_mutex);
        iforce->xmit.buf = iforce->xmit_data;
        iforce->dev = input_dev;
 
index 146f406..668f245 100644 (file)
@@ -37,7 +37,7 @@
 #include <linux/serio.h>
 #include <linux/config.h>
 #include <linux/circ_buf.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
 
 /* This module provides arbitrary resource management routines.
  * I use it to manage the device's memory.
@@ -45,6 +45,7 @@
  */
 #include <linux/ioport.h>
 
+
 #define IFORCE_MAX_LENGTH      16
 
 /* iforce::bus */
@@ -146,7 +147,7 @@ struct iforce {
        wait_queue_head_t wait;
        struct resource device_memory;
        struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
-       struct semaphore mem_mutex;
+       struct mutex mem_mutex;
 };
 
 /* Get hi and low bytes of a 16-bits int */
index b154938..5570fd5 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
+#include <linux/mutex.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("TurboGraFX parallel port interface driver");
@@ -86,7 +87,7 @@ static struct tgfx {
        char phys[TGFX_MAX_DEVICES][32];
        int sticks;
        int used;
-       struct semaphore sem;
+       struct mutex sem;
 } *tgfx_base[TGFX_MAX_PORTS];
 
 /*
@@ -128,7 +129,7 @@ static int tgfx_open(struct input_dev *dev)
        struct tgfx *tgfx = dev->private;
        int err;
 
-       err = down_interruptible(&tgfx->sem);
+       err = mutex_lock_interruptible(&tgfx->sem);
        if (err)
                return err;
 
@@ -138,7 +139,7 @@ static int tgfx_open(struct input_dev *dev)
                mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
        }
 
-       up(&tgfx->sem);
+       mutex_unlock(&tgfx->sem);
        return 0;
 }
 
@@ -146,13 +147,13 @@ static void tgfx_close(struct input_dev *dev)
 {
        struct tgfx *tgfx = dev->private;
 
-       down(&tgfx->sem);
+       mutex_lock(&tgfx->sem);
        if (!--tgfx->used) {
                del_timer_sync(&tgfx->timer);
                parport_write_control(tgfx->pd->port, 0x00);
                parport_release(tgfx->pd);
        }
-       up(&tgfx->sem);
+       mutex_unlock(&tgfx->sem);
 }
 
 
@@ -191,7 +192,7 @@ static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs)
                goto err_unreg_pardev;
        }
 
-       init_MUTEX(&tgfx->sem);
+       mutex_init(&tgfx->sem);
        tgfx->pd = pd;
        init_timer(&tgfx->timer);
        tgfx->timer.data = (long) tgfx;
index 3b0ac3b..a9dda56 100644 (file)
@@ -13,7 +13,7 @@ menuconfig INPUT_KEYBOARD
 if INPUT_KEYBOARD
 
 config KEYBOARD_ATKBD
-       tristate "AT keyboard" if !X86_PC
+       tristate "AT keyboard" if EMBEDDED || !X86_PC
        default y
        select SERIO
        select SERIO_LIBPS2
index ffacf6e..fad04b6 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/serio.h>
 #include <linux/workqueue.h>
 #include <linux/libps2.h>
+#include <linux/mutex.h>
 
 #define DRIVER_DESC    "AT and PS/2 keyboard driver"
 
@@ -216,7 +217,7 @@ struct atkbd {
        unsigned long time;
 
        struct work_struct event_work;
-       struct semaphore event_sem;
+       struct mutex event_mutex;
        unsigned long event_mask;
 };
 
@@ -302,19 +303,19 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
        if (atkbd->translated) {
 
                if (atkbd->emul ||
-                   !(code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1 ||
-                     code == ATKBD_RET_HANGUEL || code == ATKBD_RET_HANJA ||
-                    (code == ATKBD_RET_ERR && !atkbd->err_xl) ||
-                    (code == ATKBD_RET_BAT && !atkbd->bat_xl))) {
+                   (code != ATKBD_RET_EMUL0 && code != ATKBD_RET_EMUL1 &&
+                    code != ATKBD_RET_HANGUEL && code != ATKBD_RET_HANJA &&
+                    (code != ATKBD_RET_ERR || atkbd->err_xl) &&
+                    (code != ATKBD_RET_BAT || atkbd->bat_xl))) {
                        atkbd->release = code >> 7;
                        code &= 0x7f;
                }
 
                if (!atkbd->emul) {
                     if ((code & 0x7f) == (ATKBD_RET_BAT & 0x7f))
-                       atkbd->bat_xl = !atkbd->release;
+                       atkbd->bat_xl = !(data >> 7);
                     if ((code & 0x7f) == (ATKBD_RET_ERR & 0x7f))
-                       atkbd->err_xl = !atkbd->release;
+                       atkbd->err_xl = !(data >> 7);
                }
        }
 
@@ -449,7 +450,7 @@ static void atkbd_event_work(void *data)
        unsigned char param[2];
        int i, j;
 
-       down(&atkbd->event_sem);
+       mutex_lock(&atkbd->event_mutex);
 
        if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) {
                param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
@@ -480,7 +481,7 @@ static void atkbd_event_work(void *data)
                ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP);
        }
 
-       up(&atkbd->event_sem);
+       mutex_unlock(&atkbd->event_mutex);
 }
 
 /*
@@ -846,7 +847,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
        atkbd->dev = dev;
        ps2_init(&atkbd->ps2dev, serio);
        INIT_WORK(&atkbd->event_work, atkbd_event_work, atkbd);
-       init_MUTEX(&atkbd->event_sem);
+       mutex_init(&atkbd->event_mutex);
 
        switch (serio->id.type) {
 
@@ -862,9 +863,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
        atkbd->softrepeat = atkbd_softrepeat;
        atkbd->scroll = atkbd_scroll;
 
-       if (!atkbd->write)
-               atkbd->softrepeat = 1;
-
        if (atkbd->softrepeat)
                atkbd->softraw = 1;
 
index e301ee4..96c6bf7 100644 (file)
 #define KB_COLS                                12
 #define KB_ROWMASK(r)          (1 << (r))
 #define SCANCODE(r,c)          ( ((r)<<4) + (c) + 1 )
-/* zero code, 124 scancodes + 3 hinge combinations */
-#define        NR_SCANCODES            ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 +3 )
-#define SCAN_INTERVAL          (HZ/10)
+/* zero code, 124 scancodes */
+#define        NR_SCANCODES            ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 )
 
-#define HINGE_SCAN_INTERVAL            (HZ/4)
+#define SCAN_INTERVAL          (50) /* ms */
+#define HINGE_SCAN_INTERVAL    (250) /* ms */
 
 #define CORGI_KEY_CALENDER     KEY_F1
 #define CORGI_KEY_ADDRESS      KEY_F2
@@ -49,9 +49,6 @@
 #define CORGI_KEY_MAIL         KEY_F10
 #define CORGI_KEY_OK           KEY_F11
 #define CORGI_KEY_MENU         KEY_F12
-#define CORGI_HINGE_0          KEY_KP0
-#define CORGI_HINGE_1          KEY_KP1
-#define CORGI_HINGE_2          KEY_KP2
 
 static unsigned char corgikbd_keycode[NR_SCANCODES] = {
        0,                                                                                                                /* 0 */
@@ -63,7 +60,6 @@ static unsigned char corgikbd_keycode[NR_SCANCODES] = {
        CORGI_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0,            /* 81-96 */
        KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, CORGI_KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0,  /* 97-112 */
        CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0,   /* 113-124 */
-       CORGI_HINGE_0, CORGI_HINGE_1, CORGI_HINGE_2       /* 125-127 */
 };
 
 
@@ -187,7 +183,7 @@ static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data, struct pt_regs
 
        /* if any keys are pressed, enable the timer */
        if (num_pressed)
-               mod_timer(&corgikbd_data->timer, jiffies + SCAN_INTERVAL);
+               mod_timer(&corgikbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL));
 
        spin_unlock_irqrestore(&corgikbd_data->lock, flags);
 }
@@ -228,6 +224,7 @@ static void corgikbd_timer_callback(unsigned long data)
  *          0x0c - Keyboard and Screen Closed
  */
 
+#define READ_GPIO_BIT(x)    (GPLR(x) & GPIO_bit(x))
 #define HINGE_STABLE_COUNT 2
 static int sharpsl_hinge_state;
 static int hinge_count;
@@ -239,6 +236,7 @@ static void corgikbd_hinge_timer(unsigned long data)
        unsigned long flags;
 
        gprr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB);
+       gprr |= (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0);
        if (gprr != sharpsl_hinge_state) {
                hinge_count = 0;
                sharpsl_hinge_state = gprr;
@@ -249,27 +247,38 @@ static void corgikbd_hinge_timer(unsigned long data)
 
                        input_report_switch(corgikbd_data->input, SW_0, ((sharpsl_hinge_state & CORGI_SCP_SWA) != 0));
                        input_report_switch(corgikbd_data->input, SW_1, ((sharpsl_hinge_state & CORGI_SCP_SWB) != 0));
+                       input_report_switch(corgikbd_data->input, SW_2, (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0));
                        input_sync(corgikbd_data->input);
 
                        spin_unlock_irqrestore(&corgikbd_data->lock, flags);
                }
        }
-       mod_timer(&corgikbd_data->htimer, jiffies + HINGE_SCAN_INTERVAL);
+       mod_timer(&corgikbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
 }
 
 #ifdef CONFIG_PM
 static int corgikbd_suspend(struct platform_device *dev, pm_message_t state)
 {
+       int i;
        struct corgikbd *corgikbd = platform_get_drvdata(dev);
+
        corgikbd->suspended = 1;
+       /* strobe 0 is the power key so this can't be made an input for
+          powersaving therefore i = 1 */
+       for (i = 1; i < CORGI_KEY_STROBE_NUM; i++)
+               pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_IN);
 
        return 0;
 }
 
 static int corgikbd_resume(struct platform_device *dev)
 {
+       int i;
        struct corgikbd *corgikbd = platform_get_drvdata(dev);
 
+       for (i = 1; i < CORGI_KEY_STROBE_NUM; i++)
+               pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
+
        /* Upon resume, ignore the suspend key for a short while */
        corgikbd->suspend_jiffies=jiffies;
        corgikbd->suspended = 0;
@@ -333,10 +342,11 @@ static int __init corgikbd_probe(struct platform_device *pdev)
        clear_bit(0, input_dev->keybit);
        set_bit(SW_0, input_dev->swbit);
        set_bit(SW_1, input_dev->swbit);
+       set_bit(SW_2, input_dev->swbit);
 
        input_register_device(corgikbd->input);
 
-       mod_timer(&corgikbd->htimer, jiffies + HINGE_SCAN_INTERVAL);
+       mod_timer(&corgikbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
 
        /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
        for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) {
@@ -351,6 +361,9 @@ static int __init corgikbd_probe(struct platform_device *pdev)
        for (i = 0; i < CORGI_KEY_STROBE_NUM; i++)
                pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
 
+       /* Setup the headphone jack as an input */
+       pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN);
+
        return 0;
 }
 
index 63f387e..1dca3cf 100644 (file)
@@ -250,16 +250,19 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
        struct hil_kbd  *kbd;
        uint8_t         did, *idd;
        int             i;
-       
+
        kbd = kzalloc(sizeof(*kbd), GFP_KERNEL);
        if (!kbd)
                return -ENOMEM;
 
        kbd->dev = input_allocate_device();
-       if (!kbd->dev) goto bail1;
+       if (!kbd->dev)
+               goto bail0;
+
        kbd->dev->private = kbd;
 
-       if (serio_open(serio, drv)) goto bail0;
+       if (serio_open(serio, drv))
+               goto bail1;
 
        serio_set_drvdata(serio, kbd);
        kbd->serio = serio;
index 83999d5..bc61cf8 100644 (file)
@@ -30,6 +30,7 @@
 #define SCANCODE(r,c)          (((r)<<4) + (c) + 1)
 #define        NR_SCANCODES            ((KB_ROWS<<4) + 1)
 
+#define SCAN_INTERVAL          (50) /* ms */
 #define HINGE_SCAN_INTERVAL    (150) /* ms */
 
 #define SPITZ_KEY_CALENDER     KEY_F1
@@ -230,7 +231,7 @@ static void spitzkbd_scankeyboard(struct spitzkbd *spitzkbd_data, struct pt_regs
 
        /* if any keys are pressed, enable the timer */
        if (num_pressed)
-               mod_timer(&spitzkbd_data->timer, jiffies + msecs_to_jiffies(100));
+               mod_timer(&spitzkbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL));
 
        spin_unlock_irqrestore(&spitzkbd_data->lock, flags);
 }
@@ -287,6 +288,7 @@ static void spitzkbd_hinge_timer(unsigned long data)
        unsigned long flags;
 
        state = GPLR(SPITZ_GPIO_SWA) & (GPIO_bit(SPITZ_GPIO_SWA)|GPIO_bit(SPITZ_GPIO_SWB));
+       state |= (GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT));
        if (state != sharpsl_hinge_state) {
                hinge_count = 0;
                sharpsl_hinge_state = state;
@@ -299,6 +301,7 @@ static void spitzkbd_hinge_timer(unsigned long data)
 
                input_report_switch(spitzkbd_data->input, SW_0, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0));
                input_report_switch(spitzkbd_data->input, SW_1, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0));
+               input_report_switch(spitzkbd_data->input, SW_2, ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) != 0));
                input_sync(spitzkbd_data->input);
 
                spin_unlock_irqrestore(&spitzkbd_data->lock, flags);
@@ -397,6 +400,7 @@ static int __init spitzkbd_probe(struct platform_device *dev)
        clear_bit(0, input_dev->keybit);
        set_bit(SW_0, input_dev->swbit);
        set_bit(SW_1, input_dev->swbit);
+       set_bit(SW_2, input_dev->swbit);
 
        input_register_device(input_dev);
 
@@ -432,6 +436,9 @@ static int __init spitzkbd_probe(struct platform_device *dev)
        request_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd_hinge_isr,
                    SA_INTERRUPT | SA_TRIGGER_RISING | SA_TRIGGER_FALLING,
                    "Spitzkbd SWB", spitzkbd);
+       request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr,
+                   SA_INTERRUPT | SA_TRIGGER_RISING | SA_TRIGGER_FALLING,
+                   "Spitzkbd HP", spitzkbd);
 
        printk(KERN_INFO "input: Spitz Keyboard Registered\n");
 
@@ -450,6 +457,7 @@ static int spitzkbd_remove(struct platform_device *dev)
        free_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd);
        free_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd);
        free_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd);
+       free_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd);
 
        del_timer_sync(&spitzkbd->htimer);
        del_timer_sync(&spitzkbd->timer);
index 1ef477f..afd3221 100644 (file)
@@ -24,7 +24,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("PC Speaker beeper driver");
 MODULE_LICENSE("GPL");
 
-static struct platform_device *pcspkr_platform_device;
 static DEFINE_SPINLOCK(i8253_beep_lock);
 
 static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
@@ -135,35 +134,11 @@ static struct platform_driver pcspkr_platform_driver = {
 
 static int __init pcspkr_init(void)
 {
-       int err;
-
-       err = platform_driver_register(&pcspkr_platform_driver);
-       if (err)
-               return err;
-
-       pcspkr_platform_device = platform_device_alloc("pcspkr", -1);
-       if (!pcspkr_platform_device) {
-               err = -ENOMEM;
-               goto err_unregister_driver;
-       }
-
-       err = platform_device_add(pcspkr_platform_device);
-       if (err)
-               goto err_free_device;
-
-       return 0;
-
- err_free_device:
-       platform_device_put(pcspkr_platform_device);
- err_unregister_driver:
-       platform_driver_unregister(&pcspkr_platform_driver);
-
-       return err;
+       return platform_driver_register(&pcspkr_platform_driver);
 }
 
 static void __exit pcspkr_exit(void)
 {
-       platform_device_unregister(pcspkr_platform_device);
        platform_driver_unregister(&pcspkr_platform_driver);
 }
 
index 546ed9b..d723e9a 100644 (file)
@@ -194,7 +194,7 @@ static int uinput_open(struct inode *inode, struct file *file)
        if (!newdev)
                return -ENOMEM;
 
-       init_MUTEX(&newdev->sem);
+       mutex_init(&newdev->mutex);
        spin_lock_init(&newdev->requests_lock);
        init_waitqueue_head(&newdev->requests_waitq);
        init_waitqueue_head(&newdev->waitq);
@@ -340,7 +340,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t
        struct uinput_device *udev = file->private_data;
        int retval;
 
-       retval = down_interruptible(&udev->sem);
+       retval = mutex_lock_interruptible(&udev->mutex);
        if (retval)
                return retval;
 
@@ -348,7 +348,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t
                        uinput_inject_event(udev, buffer, count) :
                        uinput_setup_device(udev, buffer, count);
 
-       up(&udev->sem);
+       mutex_unlock(&udev->mutex);
 
        return retval;
 }
@@ -369,7 +369,7 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count,
        if (retval)
                return retval;
 
-       retval = down_interruptible(&udev->sem);
+       retval = mutex_lock_interruptible(&udev->mutex);
        if (retval)
                return retval;
 
@@ -388,7 +388,7 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count,
        }
 
  out:
-       up(&udev->sem);
+       mutex_unlock(&udev->mutex);
 
        return retval;
 }
@@ -439,7 +439,7 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
        udev = file->private_data;
 
-       retval = down_interruptible(&udev->sem);
+       retval = mutex_lock_interruptible(&udev->mutex);
        if (retval)
                return retval;
 
@@ -589,7 +589,7 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        }
 
  out:
-       up(&udev->sem);
+       mutex_unlock(&udev->mutex);
        return retval;
 }
 
index bfb564f..69f0217 100644 (file)
@@ -249,10 +249,13 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
                return -ENOMEM;
 
        ptr->dev = input_allocate_device();
-       if (!ptr->dev) goto bail0;
+       if (!ptr->dev)
+               goto bail0;
+
        ptr->dev->private = ptr;
 
-       if (serio_open(serio, driver)) goto bail1;
+       if (serio_open(serio, driver))
+               goto bail1;
 
        serio_set_drvdata(serio, ptr);
        ptr->serio = serio;
index ad62174..32d70ed 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/serio.h>
 #include <linux/init.h>
 #include <linux/libps2.h>
+#include <linux/mutex.h>
+
 #include "psmouse.h"
 #include "synaptics.h"
 #include "logips2pp.h"
@@ -98,13 +100,13 @@ __obsolete_setup("psmouse_resetafter=");
 __obsolete_setup("psmouse_rate=");
 
 /*
- * psmouse_sem protects all operations changing state of mouse
+ * psmouse_mutex protects all operations changing state of mouse
  * (connecting, disconnecting, changing rate or resolution via
  * sysfs). We could use a per-device semaphore but since there
  * rarely more than one PS/2 mouse connected and since semaphore
  * is taken in "slow" paths it is not worth it.
  */
-static DECLARE_MUTEX(psmouse_sem);
+static DEFINE_MUTEX(psmouse_mutex);
 
 static struct workqueue_struct *kpsmoused_wq;
 
@@ -868,7 +870,7 @@ static void psmouse_resync(void *p)
        int failed = 0, enabled = 0;
        int i;
 
-       down(&psmouse_sem);
+       mutex_lock(&psmouse_mutex);
 
        if (psmouse->state != PSMOUSE_RESYNCING)
                goto out;
@@ -948,7 +950,7 @@ static void psmouse_resync(void *p)
        if (parent)
                psmouse_activate(parent);
  out:
-       up(&psmouse_sem);
+       mutex_unlock(&psmouse_mutex);
 }
 
 /*
@@ -974,14 +976,14 @@ static void psmouse_disconnect(struct serio *serio)
 
        sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
 
-       down(&psmouse_sem);
+       mutex_lock(&psmouse_mutex);
 
        psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
 
        /* make sure we don't have a resync in progress */
-       up(&psmouse_sem);
+       mutex_unlock(&psmouse_mutex);
        flush_workqueue(kpsmoused_wq);
-       down(&psmouse_sem);
+       mutex_lock(&psmouse_mutex);
 
        if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
                parent = serio_get_drvdata(serio->parent);
@@ -1004,7 +1006,7 @@ static void psmouse_disconnect(struct serio *serio)
        if (parent)
                psmouse_activate(parent);
 
-       up(&psmouse_sem);
+       mutex_unlock(&psmouse_mutex);
 }
 
 static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_protocol *proto)
@@ -1076,7 +1078,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
        struct input_dev *input_dev;
        int retval = -ENOMEM;
 
-       down(&psmouse_sem);
+       mutex_lock(&psmouse_mutex);
 
        /*
         * If this is a pass-through port deactivate parent so the device
@@ -1144,7 +1146,7 @@ out:
        if (parent)
                psmouse_activate(parent);
 
-       up(&psmouse_sem);
+       mutex_unlock(&psmouse_mutex);
        return retval;
 }
 
@@ -1161,7 +1163,7 @@ static int psmouse_reconnect(struct serio *serio)
                return -1;
        }
 
-       down(&psmouse_sem);
+       mutex_lock(&psmouse_mutex);
 
        if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
                parent = serio_get_drvdata(serio->parent);
@@ -1195,7 +1197,7 @@ out:
        if (parent)
                psmouse_activate(parent);
 
-       up(&psmouse_sem);
+       mutex_unlock(&psmouse_mutex);
        return rc;
 }
 
@@ -1273,7 +1275,7 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
                goto out_unpin;
        }
 
-       retval = down_interruptible(&psmouse_sem);
+       retval = mutex_lock_interruptible(&psmouse_mutex);
        if (retval)
                goto out_unpin;
 
@@ -1281,7 +1283,7 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
 
        if (psmouse->state == PSMOUSE_IGNORE) {
                retval = -ENODEV;
-               goto out_up;
+               goto out_unlock;
        }
 
        if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
@@ -1299,8 +1301,8 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
        if (parent)
                psmouse_activate(parent);
 
- out_up:
-       up(&psmouse_sem);
+ out_unlock:
+       mutex_unlock(&psmouse_mutex);
  out_unpin:
        serio_unpin_driver(serio);
        return retval;
@@ -1357,11 +1359,11 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
                        return -EIO;
                }
 
-               up(&psmouse_sem);
+               mutex_unlock(&psmouse_mutex);
                serio_unpin_driver(serio);
                serio_unregister_child_port(serio);
                serio_pin_driver_uninterruptible(serio);
-               down(&psmouse_sem);
+               mutex_lock(&psmouse_mutex);
 
                if (serio->drv != &psmouse_drv) {
                        input_free_device(new_dev);
index 2051bec..ad5d0a8 100644 (file)
@@ -247,14 +247,12 @@ static void synaptics_pt_create(struct psmouse *psmouse)
 {
        struct serio *serio;
 
-       serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+       serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
        if (!serio) {
                printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
                return;
        }
 
-       memset(serio, 0, sizeof(struct serio));
-
        serio->id.type = SERIO_PS_PSTHRU;
        strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
        strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
@@ -605,14 +603,21 @@ static struct dmi_system_id toshiba_dmi_table[] = {
                .ident = "Toshiba Satellite",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
-                       DMI_MATCH(DMI_PRODUCT_NAME , "Satellite"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"),
                },
        },
        {
                .ident = "Toshiba Dynabook",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
-                       DMI_MATCH(DMI_PRODUCT_NAME , "dynabook"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"),
+               },
+       },
+       {
+               .ident = "Toshiba Portege M300",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"),
                },
        },
        { }
@@ -623,10 +628,9 @@ int synaptics_init(struct psmouse *psmouse)
 {
        struct synaptics_data *priv;
 
-       psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL);
+       psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
        if (!priv)
                return -1;
-       memset(priv, 0, sizeof(struct synaptics_data));
 
        if (synaptics_query_hardware(psmouse)) {
                printk(KERN_ERR "Unable to query Synaptics hardware.\n");
index 9abed18..b685a50 100644 (file)
@@ -412,9 +412,8 @@ static int mousedev_open(struct inode * inode, struct file * file)
        if (i >= MOUSEDEV_MINORS || !mousedev_table[i])
                return -ENODEV;
 
-       if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
+       if (!(list = kzalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
                return -ENOMEM;
-       memset(list, 0, sizeof(struct mousedev_list));
 
        spin_lock_init(&list->packet_lock);
        list->pos_x = xres / 2;
@@ -626,9 +625,8 @@ static struct input_handle *mousedev_connect(struct input_handler *handler, stru
                return NULL;
        }
 
-       if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL)))
+       if (!(mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL)))
                return NULL;
-       memset(mousedev, 0, sizeof(struct mousedev));
 
        INIT_LIST_HEAD(&mousedev->list);
        init_waitqueue_head(&mousedev->wait);
index bfc5c63..526e607 100644 (file)
@@ -103,9 +103,8 @@ static struct input_handle *power_connect(struct input_handler *handler,
 {
        struct input_handle *handle;
 
-       if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL)))
+       if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL)))
                return NULL;
-       memset(handle, 0, sizeof(struct input_handle));
 
        handle->dev = dev;
        handle->handler = handler;
index ea49978..bbbe15e 100644 (file)
@@ -872,9 +872,8 @@ int hil_mlc_register(hil_mlc *mlc) {
        for (i = 0; i < HIL_MLC_DEVMEM; i++) {
                struct serio *mlc_serio;
                hil_mlc_copy_di_scratch(mlc, i);
-               mlc_serio = kmalloc(sizeof(*mlc_serio), GFP_KERNEL);
+               mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL);
                mlc->serio[i] = mlc_serio;
-               memset(mlc_serio, 0, sizeof(*mlc_serio));
                mlc_serio->id                   = hil_mlc_serio_id;
                mlc_serio->write                = hil_mlc_serio_write;
                mlc_serio->open                 = hil_mlc_serio_open;
index a4c6f35..f606e96 100644 (file)
@@ -192,7 +192,9 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
 #include <linux/pnp.h>
 
 static int i8042_pnp_kbd_registered;
+static unsigned int i8042_pnp_kbd_devices;
 static int i8042_pnp_aux_registered;
+static unsigned int i8042_pnp_aux_devices;
 
 static int i8042_pnp_command_reg;
 static int i8042_pnp_data_reg;
@@ -219,6 +221,7 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *
                strncat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name));
        }
 
+       i8042_pnp_kbd_devices++;
        return 0;
 }
 
@@ -239,6 +242,7 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *
                strncat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name));
        }
 
+       i8042_pnp_aux_devices++;
        return 0;
 }
 
@@ -287,21 +291,23 @@ static void i8042_pnp_exit(void)
 
 static int __init i8042_pnp_init(void)
 {
-       int result_kbd = 0, result_aux = 0;
        char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 };
+       int err;
 
        if (i8042_nopnp) {
                printk(KERN_INFO "i8042: PNP detection disabled\n");
                return 0;
        }
 
-       if ((result_kbd = pnp_register_driver(&i8042_pnp_kbd_driver)) >= 0)
+       err = pnp_register_driver(&i8042_pnp_kbd_driver);
+       if (!err)
                i8042_pnp_kbd_registered = 1;
 
-       if ((result_aux = pnp_register_driver(&i8042_pnp_aux_driver)) >= 0)
+       err = pnp_register_driver(&i8042_pnp_aux_driver);
+       if (!err)
                i8042_pnp_aux_registered = 1;
 
-       if (result_kbd <= 0 && result_aux <= 0) {
+       if (!i8042_pnp_kbd_devices && !i8042_pnp_aux_devices) {
                i8042_pnp_exit();
 #if defined(__ia64__)
                return -ENODEV;
@@ -311,24 +317,24 @@ static int __init i8042_pnp_init(void)
 #endif
        }
 
-       if (result_kbd > 0)
+       if (i8042_pnp_kbd_devices)
                snprintf(kbd_irq_str, sizeof(kbd_irq_str),
                        "%d", i8042_pnp_kbd_irq);
-       if (result_aux > 0)
+       if (i8042_pnp_aux_devices)
                snprintf(aux_irq_str, sizeof(aux_irq_str),
                        "%d", i8042_pnp_aux_irq);
 
        printk(KERN_INFO "PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n",
-               i8042_pnp_kbd_name, (result_kbd > 0 && result_aux > 0) ? "," : "",
+               i8042_pnp_kbd_name, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "",
                i8042_pnp_aux_name,
                i8042_pnp_data_reg, i8042_pnp_command_reg,
-               kbd_irq_str, (result_kbd > 0 && result_aux > 0) ? "," : "",
+               kbd_irq_str, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "",
                aux_irq_str);
 
 #if defined(__ia64__)
-       if (result_kbd <= 0)
+       if (!i8042_pnp_kbd_devices)
                i8042_nokbd = 1;
-       if (result_aux <= 0)
+       if (!i8042_pnp_aux_devices)
                i8042_noaux = 1;
 #endif
 
index d4c990f..79c97f9 100644 (file)
@@ -84,7 +84,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
                maxbytes = sizeof(ps2dev->cmdbuf);
        }
 
-       down(&ps2dev->cmd_sem);
+       mutex_lock(&ps2dev->cmd_mutex);
 
        serio_pause_rx(ps2dev->serio);
        ps2dev->flags = PS2_FLAG_CMD;
@@ -94,7 +94,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
        wait_event_timeout(ps2dev->wait,
                           !(ps2dev->flags & PS2_FLAG_CMD),
                           msecs_to_jiffies(timeout));
-       up(&ps2dev->cmd_sem);
+       mutex_unlock(&ps2dev->cmd_mutex);
 }
 
 /*
@@ -177,7 +177,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
                return -1;
        }
 
-       down(&ps2dev->cmd_sem);
+       mutex_lock(&ps2dev->cmd_mutex);
 
        serio_pause_rx(ps2dev->serio);
        ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
@@ -229,7 +229,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
        ps2dev->flags = 0;
        serio_continue_rx(ps2dev->serio);
 
-       up(&ps2dev->cmd_sem);
+       mutex_unlock(&ps2dev->cmd_mutex);
        return rc;
 }
 
@@ -281,7 +281,7 @@ int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int comman
 
 void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
 {
-       init_MUTEX(&ps2dev->cmd_sem);
+       mutex_init(&ps2dev->cmd_mutex);
        init_waitqueue_head(&ps2dev->wait);
        ps2dev->serio = serio;
 }
index 1d15c28..a5c1fb3 100644 (file)
@@ -171,9 +171,8 @@ static struct serio * __init parkbd_allocate_serio(void)
 {
        struct serio *serio;
 
-       serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+       serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
        if (serio) {
-               memset(serio, 0, sizeof(struct serio));
                serio->id.type = parkbd_mode;
                serio->write = parkbd_write,
                strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name));
index a3bd115..513d37f 100644 (file)
@@ -111,11 +111,10 @@ static int __devinit rpckbd_probe(struct platform_device *dev)
 {
        struct serio *serio;
 
-       serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+       serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
        if (!serio)
                return -ENOMEM;
 
-       memset(serio, 0, sizeof(struct serio));
        serio->id.type          = SERIO_8042;
        serio->write            = rpckbd_write;
        serio->open             = rpckbd_open;
index 2f76813..6521034 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/kthread.h>
+#include <linux/mutex.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("Serio abstraction core");
@@ -52,10 +53,10 @@ EXPORT_SYMBOL(serio_rescan);
 EXPORT_SYMBOL(serio_reconnect);
 
 /*
- * serio_sem protects entire serio subsystem and is taken every time
+ * serio_mutex protects entire serio subsystem and is taken every time
  * serio port or driver registrered or unregistered.
  */
-static DECLARE_MUTEX(serio_sem);
+static DEFINE_MUTEX(serio_mutex);
 
 static LIST_HEAD(serio_list);
 
@@ -70,9 +71,9 @@ static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
 {
        int retval;
 
-       down(&serio->drv_sem);
+       mutex_lock(&serio->drv_mutex);
        retval = drv->connect(serio, drv);
-       up(&serio->drv_sem);
+       mutex_unlock(&serio->drv_mutex);
 
        return retval;
 }
@@ -81,20 +82,20 @@ static int serio_reconnect_driver(struct serio *serio)
 {
        int retval = -1;
 
-       down(&serio->drv_sem);
+       mutex_lock(&serio->drv_mutex);
        if (serio->drv && serio->drv->reconnect)
                retval = serio->drv->reconnect(serio);
-       up(&serio->drv_sem);
+       mutex_unlock(&serio->drv_mutex);
 
        return retval;
 }
 
 static void serio_disconnect_driver(struct serio *serio)
 {
-       down(&serio->drv_sem);
+       mutex_lock(&serio->drv_mutex);
        if (serio->drv)
                serio->drv->disconnect(serio);
-       up(&serio->drv_sem);
+       mutex_unlock(&serio->drv_mutex);
 }
 
 static int serio_match_port(const struct serio_device_id *ids, struct serio *serio)
@@ -195,6 +196,7 @@ static void serio_queue_event(void *object, struct module *owner,
        if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) {
                if (!try_module_get(owner)) {
                        printk(KERN_WARNING "serio: Can't get module reference, dropping event %d\n", event_type);
+                       kfree(event);
                        goto out;
                }
 
@@ -272,7 +274,7 @@ static void serio_handle_event(void)
        struct serio_event *event;
        struct serio_driver *serio_drv;
 
-       down(&serio_sem);
+       mutex_lock(&serio_mutex);
 
        /*
         * Note that we handle only one event here to give swsusp
@@ -314,7 +316,7 @@ static void serio_handle_event(void)
                serio_free_event(event);
        }
 
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 }
 
 /*
@@ -449,7 +451,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
        struct device_driver *drv;
        int retval;
 
-       retval = down_interruptible(&serio_sem);
+       retval = mutex_lock_interruptible(&serio_mutex);
        if (retval)
                return retval;
 
@@ -469,7 +471,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
                retval = -EINVAL;
        }
 
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 
        return retval;
 }
@@ -524,7 +526,7 @@ static void serio_init_port(struct serio *serio)
        __module_get(THIS_MODULE);
 
        spin_lock_init(&serio->lock);
-       init_MUTEX(&serio->drv_sem);
+       mutex_init(&serio->drv_mutex);
        device_initialize(&serio->dev);
        snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id),
                 "serio%ld", (long)atomic_inc_return(&serio_no) - 1);
@@ -661,10 +663,10 @@ void __serio_register_port(struct serio *serio, struct module *owner)
  */
 void serio_unregister_port(struct serio *serio)
 {
-       down(&serio_sem);
+       mutex_lock(&serio_mutex);
        serio_disconnect_port(serio);
        serio_destroy_port(serio);
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 }
 
 /*
@@ -672,17 +674,17 @@ void serio_unregister_port(struct serio *serio)
  */
 void serio_unregister_child_port(struct serio *serio)
 {
-       down(&serio_sem);
+       mutex_lock(&serio_mutex);
        if (serio->child) {
                serio_disconnect_port(serio->child);
                serio_destroy_port(serio->child);
        }
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 }
 
 /*
  * Submits register request to kseriod for subsequent execution.
- * Can be used when it is not obvious whether the serio_sem is
+ * Can be used when it is not obvious whether the serio_mutex is
  * taken or not and when delayed execution is feasible.
  */
 void __serio_unregister_port_delayed(struct serio *serio, struct module *owner)
@@ -765,7 +767,7 @@ void serio_unregister_driver(struct serio_driver *drv)
 {
        struct serio *serio;
 
-       down(&serio_sem);
+       mutex_lock(&serio_mutex);
        drv->manual_bind = 1;   /* so serio_find_driver ignores it */
 
 start_over:
@@ -779,7 +781,7 @@ start_over:
        }
 
        driver_unregister(&drv->driver);
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 }
 
 static void serio_set_drv(struct serio *serio, struct serio_driver *drv)
@@ -858,7 +860,7 @@ static int serio_resume(struct device *dev)
        return 0;
 }
 
-/* called from serio_driver->connect/disconnect methods under serio_sem */
+/* called from serio_driver->connect/disconnect methods under serio_mutex */
 int serio_open(struct serio *serio, struct serio_driver *drv)
 {
        serio_set_drv(serio, drv);
@@ -870,7 +872,7 @@ int serio_open(struct serio *serio, struct serio_driver *drv)
        return 0;
 }
 
-/* called from serio_driver->connect/disconnect methods under serio_sem */
+/* called from serio_driver->connect/disconnect methods under serio_mutex */
 void serio_close(struct serio *serio)
 {
        if (serio->close)
@@ -923,5 +925,5 @@ static void __exit serio_exit(void)
        kthread_stop(serio_task);
 }
 
-module_init(serio_init);
+subsys_initcall(serio_init);
 module_exit(serio_exit);
index 47e08de..5a2703b 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/devfs_fs_kernel.h>
 #include <linux/miscdevice.h>
 #include <linux/wait.h>
+#include <linux/mutex.h>
 
 #define DRIVER_DESC    "Raw serio driver"
 
@@ -46,7 +47,7 @@ struct serio_raw_list {
        struct list_head node;
 };
 
-static DECLARE_MUTEX(serio_raw_sem);
+static DEFINE_MUTEX(serio_raw_mutex);
 static LIST_HEAD(serio_raw_list);
 static unsigned int serio_raw_no;
 
@@ -81,7 +82,7 @@ static int serio_raw_open(struct inode *inode, struct file *file)
        struct serio_raw_list *list;
        int retval = 0;
 
-       retval = down_interruptible(&serio_raw_sem);
+       retval = mutex_lock_interruptible(&serio_raw_mutex);
        if (retval)
                return retval;
 
@@ -95,12 +96,11 @@ static int serio_raw_open(struct inode *inode, struct file *file)
                goto out;
        }
 
-       if (!(list = kmalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) {
+       if (!(list = kzalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) {
                retval = -ENOMEM;
                goto out;
        }
 
-       memset(list, 0, sizeof(struct serio_raw_list));
        list->serio_raw = serio_raw;
        file->private_data = list;
 
@@ -108,7 +108,7 @@ static int serio_raw_open(struct inode *inode, struct file *file)
        list_add_tail(&list->node, &serio_raw->list);
 
 out:
-       up(&serio_raw_sem);
+       mutex_unlock(&serio_raw_mutex);
        return retval;
 }
 
@@ -130,12 +130,12 @@ static int serio_raw_release(struct inode *inode, struct file *file)
        struct serio_raw_list *list = file->private_data;
        struct serio_raw *serio_raw = list->serio_raw;
 
-       down(&serio_raw_sem);
+       mutex_lock(&serio_raw_mutex);
 
        serio_raw_fasync(-1, file, 0);
        serio_raw_cleanup(serio_raw);
 
-       up(&serio_raw_sem);
+       mutex_unlock(&serio_raw_mutex);
        return 0;
 }
 
@@ -194,7 +194,7 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer, siz
        int retval;
        unsigned char c;
 
-       retval = down_interruptible(&serio_raw_sem);
+       retval = mutex_lock_interruptible(&serio_raw_mutex);
        if (retval)
                return retval;
 
@@ -219,7 +219,7 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer, siz
        };
 
 out:
-       up(&serio_raw_sem);
+       mutex_unlock(&serio_raw_mutex);
        return written;
 }
 
@@ -275,14 +275,13 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
        struct serio_raw *serio_raw;
        int err;
 
-       if (!(serio_raw = kmalloc(sizeof(struct serio_raw), GFP_KERNEL))) {
+       if (!(serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL))) {
                printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n");
                return -ENOMEM;
        }
 
-       down(&serio_raw_sem);
+       mutex_lock(&serio_raw_mutex);
 
-       memset(serio_raw, 0, sizeof(struct serio_raw));
        snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++);
        serio_raw->refcnt = 1;
        serio_raw->serio = serio;
@@ -325,7 +324,7 @@ out_free:
        serio_set_drvdata(serio, NULL);
        kfree(serio_raw);
 out:
-       up(&serio_raw_sem);
+       mutex_unlock(&serio_raw_mutex);
        return err;
 }
 
@@ -350,7 +349,7 @@ static void serio_raw_disconnect(struct serio *serio)
 {
        struct serio_raw *serio_raw;
 
-       down(&serio_raw_sem);
+       mutex_lock(&serio_raw_mutex);
 
        serio_raw = serio_get_drvdata(serio);
 
@@ -361,7 +360,7 @@ static void serio_raw_disconnect(struct serio *serio)
        if (!serio_raw_cleanup(serio_raw))
                wake_up_interruptible(&serio_raw->wait);
 
-       up(&serio_raw_sem);
+       mutex_unlock(&serio_raw_mutex);
 }
 
 static struct serio_device_id serio_raw_serio_ids[] = {
index ca15479..d678d14 100644 (file)
@@ -157,9 +157,8 @@ static int tsdev_open(struct inode *inode, struct file *file)
        if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK])
                return -ENODEV;
 
-       if (!(list = kmalloc(sizeof(struct tsdev_list), GFP_KERNEL)))
+       if (!(list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL)))
                return -ENOMEM;
-       memset(list, 0, sizeof(struct tsdev_list));
 
        list->raw = (i >= TSDEV_MINORS/2) ? 1 : 0;
 
@@ -379,9 +378,8 @@ static struct input_handle *tsdev_connect(struct input_handler *handler,
                return NULL;
        }
 
-       if (!(tsdev = kmalloc(sizeof(struct tsdev), GFP_KERNEL)))
+       if (!(tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL)))
                return NULL;
-       memset(tsdev, 0, sizeof(struct tsdev));
 
        INIT_LIST_HEAD(&tsdev->list);
        init_waitqueue_head(&tsdev->wait);
index 2a2b03f..7bbfd85 100644 (file)
@@ -51,8 +51,8 @@ MODULE_LICENSE("GPL");
    handler.
 */
 
-static void avmcs_config(dev_link_t *link);
-static void avmcs_release(dev_link_t *link);
+static int avmcs_config(struct pcmcia_device *link);
+static void avmcs_release(struct pcmcia_device *link);
 
 /*
    The attach() and detach() entry points are used to create and destroy
@@ -65,10 +65,10 @@ static void avmcs_detach(struct pcmcia_device *p_dev);
 /*
    A linked list of "instances" of the skeleton device.  Each actual
    PCMCIA card corresponds to one device instance, and is described
-   by one dev_link_t structure (defined in ds.h).
+   by one struct pcmcia_device structure (defined in ds.h).
 
    You may not want to use a linked list for this -- for example, the
-   memory card driver uses an array of dev_link_t pointers, where minor
+   memory card driver uses an array of struct pcmcia_device pointers, where minor
    device numbers are used to derive the corresponding array index.
 */
 
@@ -78,7 +78,7 @@ static void avmcs_detach(struct pcmcia_device *p_dev);
    example, ethernet cards, modems).  In other cases, there may be
    many actual or logical devices (SCSI adapters, memory cards with
    multiple partitions).  The dev_node_t structures need to be kept
-   in a linked list starting at the 'dev' field of a dev_link_t
+   in a linked list starting at the 'dev' field of a struct pcmcia_device
    structure.  We allocate them in the card's private data structure,
    because they generally can't be allocated dynamically.
 */
@@ -99,54 +99,38 @@ typedef struct local_info_t {
     
 ======================================================================*/
 
-static int avmcs_attach(struct pcmcia_device *p_dev)
+static int avmcs_probe(struct pcmcia_device *p_dev)
 {
-    dev_link_t *link;
     local_info_t *local;
 
-    /* Initialize the dev_link_t structure */
-    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
-    if (!link)
-        goto err;
-    memset(link, 0, sizeof(struct dev_link_t));
-
     /* The io structure describes IO port mapping */
-    link->io.NumPorts1 = 16;
-    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-    link->io.NumPorts2 = 0;
+    p_dev->io.NumPorts1 = 16;
+    p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    p_dev->io.NumPorts2 = 0;
 
     /* Interrupt setup */
-    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
-    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+    p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+
+    p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
 
-    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
-    
     /* General socket configuration */
-    link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
-    link->conf.IntType = INT_MEMORY_AND_IO;
-    link->conf.ConfigIndex = 1;
-    link->conf.Present = PRESENT_OPTION;
+    p_dev->conf.Attributes = CONF_ENABLE_IRQ;
+    p_dev->conf.IntType = INT_MEMORY_AND_IO;
+    p_dev->conf.ConfigIndex = 1;
+    p_dev->conf.Present = PRESENT_OPTION;
 
     /* Allocate space for private device-specific data */
     local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
     if (!local)
-        goto err_kfree;
+        goto err;
     memset(local, 0, sizeof(local_info_t));
-    link->priv = local;
+    p_dev->priv = local;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
+    return avmcs_config(p_dev);
 
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    avmcs_config(link);
-
-    return 0;
-
- err_kfree:
-    kfree(link);
  err:
-    return -EINVAL;
+    return -ENOMEM;
 } /* avmcs_attach */
 
 /*======================================================================
@@ -158,15 +142,10 @@ static int avmcs_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void avmcs_detach(struct pcmcia_device *p_dev)
+static void avmcs_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-
-    if (link->state & DEV_CONFIG)
        avmcs_release(link);
-
-    kfree(link->priv);
-    kfree(link);
+       kfree(link->priv);
 } /* avmcs_detach */
 
 /*======================================================================
@@ -177,7 +156,7 @@ static void avmcs_detach(struct pcmcia_device *p_dev)
     
 ======================================================================*/
 
-static int get_tuple(client_handle_t handle, tuple_t *tuple,
+static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                     cisparse_t *parse)
 {
     int i = pcmcia_get_tuple_data(handle, tuple);
@@ -185,7 +164,7 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple,
     return pcmcia_parse_tuple(handle, tuple, parse);
 }
 
-static int first_tuple(client_handle_t handle, tuple_t *tuple,
+static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                     cisparse_t *parse)
 {
     int i = pcmcia_get_first_tuple(handle, tuple);
@@ -193,7 +172,7 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple,
     return get_tuple(handle, tuple, parse);
 }
 
-static int next_tuple(client_handle_t handle, tuple_t *tuple,
+static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                     cisparse_t *parse)
 {
     int i = pcmcia_get_next_tuple(handle, tuple);
@@ -201,9 +180,8 @@ static int next_tuple(client_handle_t handle, tuple_t *tuple,
     return get_tuple(handle, tuple, parse);
 }
 
-static void avmcs_config(dev_link_t *link)
+static int avmcs_config(struct pcmcia_device *link)
 {
-    client_handle_t handle;
     tuple_t tuple;
     cisparse_t parse;
     cistpl_cftable_entry_t *cf = &parse.cftable_entry;
@@ -213,8 +191,7 @@ static void avmcs_config(dev_link_t *link)
     char devname[128];
     int cardtype;
     int (*addcard)(unsigned int port, unsigned irq);
-    
-    handle = link->handle;
+
     dev = link->priv;
 
     /*
@@ -223,25 +200,21 @@ static void avmcs_config(dev_link_t *link)
     */
     do {
        tuple.DesiredTuple = CISTPL_CONFIG;
-       i = pcmcia_get_first_tuple(handle, &tuple);
+       i = pcmcia_get_first_tuple(link, &tuple);
        if (i != CS_SUCCESS) break;
        tuple.TupleData = buf;
        tuple.TupleDataMax = 64;
        tuple.TupleOffset = 0;
-       i = pcmcia_get_tuple_data(handle, &tuple);
+       i = pcmcia_get_tuple_data(link, &tuple);
        if (i != CS_SUCCESS) break;
-       i = pcmcia_parse_tuple(handle, &tuple, &parse);
+       i = pcmcia_parse_tuple(link, &tuple, &parse);
        if (i != CS_SUCCESS) break;
        link->conf.ConfigBase = parse.config.base;
     } while (0);
     if (i != CS_SUCCESS) {
-       cs_error(link->handle, ParseTuple, i);
-       link->state &= ~DEV_CONFIG_PENDING;
-       return;
+       cs_error(link, ParseTuple, i);
+       return -ENODEV;
     }
-    
-    /* Configure card */
-    link->state |= DEV_CONFIG;
 
     do {
 
@@ -252,7 +225,7 @@ static void avmcs_config(dev_link_t *link)
        tuple.DesiredTuple = CISTPL_VERS_1;
 
        devname[0] = 0;
-       if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) {
+       if( !first_tuple(link, &tuple, &parse) && parse.version_1.ns > 1 ) {
            strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1], 
                        sizeof(devname));
        }
@@ -263,7 +236,7 @@ static void avmcs_config(dev_link_t *link)
        tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
        tuple.Attributes = 0;
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       i = first_tuple(handle, &tuple, &parse);
+       i = first_tuple(link, &tuple, &parse);
        while (i == CS_SUCCESS) {
            if (cf->io.nwin > 0) {
                link->conf.ConfigIndex = cf->index;
@@ -273,36 +246,36 @@ static void avmcs_config(dev_link_t *link)
                 printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
                        link->io.BasePort1,
                        link->io.BasePort1+link->io.NumPorts1-1);
-               i = pcmcia_request_io(link->handle, &link->io);
+               i = pcmcia_request_io(link, &link->io);
                if (i == CS_SUCCESS) goto found_port;
            }
-           i = next_tuple(handle, &tuple, &parse);
+           i = next_tuple(link, &tuple, &parse);
        }
 
 found_port:
        if (i != CS_SUCCESS) {
-           cs_error(link->handle, RequestIO, i);
+           cs_error(link, RequestIO, i);
            break;
        }
-       
+
        /*
         * allocate an interrupt line
         */
-       i = pcmcia_request_irq(link->handle, &link->irq);
+       i = pcmcia_request_irq(link, &link->irq);
        if (i != CS_SUCCESS) {
-           cs_error(link->handle, RequestIRQ, i);
-           pcmcia_release_io(link->handle, &link->io);
+           cs_error(link, RequestIRQ, i);
+           /* undo */
+           pcmcia_disable_device(link);
            break;
        }
-       
+
        /*
          * configure the PCMCIA socket
          */
-       i = pcmcia_request_configuration(link->handle, &link->conf);
+       i = pcmcia_request_configuration(link, &link->conf);
        if (i != CS_SUCCESS) {
-           cs_error(link->handle, RequestConfiguration, i);
-           pcmcia_release_io(link->handle, &link->io);
-           pcmcia_release_irq(link->handle, &link->irq);
+           cs_error(link, RequestConfiguration, i);
+           pcmcia_disable_device(link);
            break;
        }
 
@@ -331,13 +304,12 @@ found_port:
 
     dev->node.major = 64;
     dev->node.minor = 0;
-    link->dev = &dev->node;
-    
-    link->state &= ~DEV_CONFIG_PENDING;
+    link->dev_node = &dev->node;
+
     /* If any step failed, release any partially configured state */
     if (i != 0) {
        avmcs_release(link);
-       return;
+       return -ENODEV;
     }
 
 
@@ -351,9 +323,10 @@ found_port:
         printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n",
                dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ);
        avmcs_release(link);
-       return;
+       return -ENODEV;
     }
     dev->node.minor = i;
+    return 0;
 
 } /* avmcs_config */
 
@@ -365,56 +338,12 @@ found_port:
     
 ======================================================================*/
 
-static void avmcs_release(dev_link_t *link)
+static void avmcs_release(struct pcmcia_device *link)
 {
-    b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ);
-
-    /* Unlink the device chain */
-    link->dev = NULL;
-    
-    /* Don't bother checking to see if these succeed or not */
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    link->state &= ~DEV_CONFIG;
+       b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ);
+       pcmcia_disable_device(link);
 } /* avmcs_release */
 
-static int avmcs_suspend(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int avmcs_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-       return 0;
-}
-
-/*======================================================================
-
-    The card status event handler.  Mostly, this schedules other
-    stuff to run after an event is received.  A CARD_REMOVAL event
-    also sets some flags to discourage the net drivers from trying
-    to talk to the card any more.
-
-    When a CARD_REMOVAL event is received, we immediately set a flag
-    to block future accesses to this device.  All the functions that
-    actually access the device should check this flag to make sure
-    the card is still present.
-    
-======================================================================*/
-
 
 static struct pcmcia_device_id avmcs_ids[] = {
        PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
@@ -429,11 +358,9 @@ static struct pcmcia_driver avmcs_driver = {
        .drv    = {
                .name   = "avm_cs",
        },
-       .probe = avmcs_attach,
+       .probe = avmcs_probe,
        .remove = avmcs_detach,
        .id_table = avmcs_ids,
-       .suspend= avmcs_suspend,
-       .resume = avmcs_resume,
 };
 
 static int __init avmcs_init(void)
index 969da40..ac28e32 100644 (file)
@@ -67,8 +67,8 @@ module_param(isdnprot, int, 0);
    handler.
 */
 
-static void avma1cs_config(dev_link_t *link);
-static void avma1cs_release(dev_link_t *link);
+static int avma1cs_config(struct pcmcia_device *link);
+static void avma1cs_release(struct pcmcia_device *link);
 
 /*
    The attach() and detach() entry points are used to create and destroy
@@ -82,10 +82,10 @@ static void avma1cs_detach(struct pcmcia_device *p_dev);
 /*
    A linked list of "instances" of the skeleton device.  Each actual
    PCMCIA card corresponds to one device instance, and is described
-   by one dev_link_t structure (defined in ds.h).
+   by one struct pcmcia_device structure (defined in ds.h).
 
    You may not want to use a linked list for this -- for example, the
-   memory card driver uses an array of dev_link_t pointers, where minor
+   memory card driver uses an array of struct pcmcia_device pointers, where minor
    device numbers are used to derive the corresponding array index.
 */
 
@@ -95,7 +95,7 @@ static void avma1cs_detach(struct pcmcia_device *p_dev);
    example, ethernet cards, modems).  In other cases, there may be
    many actual or logical devices (SCSI adapters, memory cards with
    multiple partitions).  The dev_node_t structures need to be kept
-   in a linked list starting at the 'dev' field of a dev_link_t
+   in a linked list starting at the 'dev' field of a struct pcmcia_device
    structure.  We allocate them in the card's private data structure,
    because they generally can't be allocated dynamically.
 */
@@ -116,55 +116,40 @@ typedef struct local_info_t {
     
 ======================================================================*/
 
-static int avma1cs_attach(struct pcmcia_device *p_dev)
+static int avma1cs_probe(struct pcmcia_device *p_dev)
 {
-    dev_link_t *link;
     local_info_t *local;
 
     DEBUG(0, "avma1cs_attach()\n");
 
-    /* Initialize the dev_link_t structure */
-    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
-    if (!link)
-       return -ENOMEM;
-    memset(link, 0, sizeof(struct dev_link_t));
-
     /* Allocate space for private device-specific data */
     local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
-    if (!local) {
-       kfree(link);
+    if (!local)
        return -ENOMEM;
-    }
+
     memset(local, 0, sizeof(local_info_t));
-    link->priv = local;
+    p_dev->priv = local;
 
     /* The io structure describes IO port mapping */
-    link->io.NumPorts1 = 16;
-    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-    link->io.NumPorts2 = 16;
-    link->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
-    link->io.IOAddrLines = 5;
+    p_dev->io.NumPorts1 = 16;
+    p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    p_dev->io.NumPorts2 = 16;
+    p_dev->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
+    p_dev->io.IOAddrLines = 5;
 
     /* Interrupt setup */
-    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
-    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+    p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
 
-    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
 
     /* General socket configuration */
-    link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
-    link->conf.IntType = INT_MEMORY_AND_IO;
-    link->conf.ConfigIndex = 1;
-    link->conf.Present = PRESENT_OPTION;
+    p_dev->conf.Attributes = CONF_ENABLE_IRQ;
+    p_dev->conf.IntType = INT_MEMORY_AND_IO;
+    p_dev->conf.ConfigIndex = 1;
+    p_dev->conf.Present = PRESENT_OPTION;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    avma1cs_config(link);
-
-    return 0;
+    return avma1cs_config(p_dev);
 } /* avma1cs_attach */
 
 /*======================================================================
@@ -176,17 +161,11 @@ static int avma1cs_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void avma1cs_detach(struct pcmcia_device *p_dev)
+static void avma1cs_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-
-    DEBUG(0, "avma1cs_detach(0x%p)\n", link);
-
-    if (link->state & DEV_CONFIG)
-           avma1cs_release(link);
-
-    kfree(link->priv);
-    kfree(link);
+       DEBUG(0, "avma1cs_detach(0x%p)\n", link);
+       avma1cs_release(link);
+       kfree(link->priv);
 } /* avma1cs_detach */
 
 /*======================================================================
@@ -197,7 +176,7 @@ static void avma1cs_detach(struct pcmcia_device *p_dev)
     
 ======================================================================*/
 
-static int get_tuple(client_handle_t handle, tuple_t *tuple,
+static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                     cisparse_t *parse)
 {
     int i = pcmcia_get_tuple_data(handle, tuple);
@@ -205,7 +184,7 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple,
     return pcmcia_parse_tuple(handle, tuple, parse);
 }
 
-static int first_tuple(client_handle_t handle, tuple_t *tuple,
+static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                     cisparse_t *parse)
 {
     int i = pcmcia_get_first_tuple(handle, tuple);
@@ -213,7 +192,7 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple,
     return get_tuple(handle, tuple, parse);
 }
 
-static int next_tuple(client_handle_t handle, tuple_t *tuple,
+static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                     cisparse_t *parse)
 {
     int i = pcmcia_get_next_tuple(handle, tuple);
@@ -221,9 +200,8 @@ static int next_tuple(client_handle_t handle, tuple_t *tuple,
     return get_tuple(handle, tuple, parse);
 }
 
-static void avma1cs_config(dev_link_t *link)
+static int avma1cs_config(struct pcmcia_device *link)
 {
-    client_handle_t handle;
     tuple_t tuple;
     cisparse_t parse;
     cistpl_cftable_entry_t *cf = &parse.cftable_entry;
@@ -233,8 +211,7 @@ static void avma1cs_config(dev_link_t *link)
     char devname[128];
     IsdnCard_t icard;
     int busy = 0;
-    
-    handle = link->handle;
+
     dev = link->priv;
 
     DEBUG(0, "avma1cs_config(0x%p)\n", link);
@@ -245,25 +222,21 @@ static void avma1cs_config(dev_link_t *link)
     */
     do {
        tuple.DesiredTuple = CISTPL_CONFIG;
-       i = pcmcia_get_first_tuple(handle, &tuple);
+       i = pcmcia_get_first_tuple(link, &tuple);
        if (i != CS_SUCCESS) break;
        tuple.TupleData = buf;
        tuple.TupleDataMax = 64;
        tuple.TupleOffset = 0;
-       i = pcmcia_get_tuple_data(handle, &tuple);
+       i = pcmcia_get_tuple_data(link, &tuple);
        if (i != CS_SUCCESS) break;
-       i = pcmcia_parse_tuple(handle, &tuple, &parse);
+       i = pcmcia_parse_tuple(link, &tuple, &parse);
        if (i != CS_SUCCESS) break;
        link->conf.ConfigBase = parse.config.base;
     } while (0);
     if (i != CS_SUCCESS) {
-       cs_error(link->handle, ParseTuple, i);
-       link->state &= ~DEV_CONFIG_PENDING;
-       return;
+       cs_error(link, ParseTuple, i);
+       return -ENODEV;
     }
-    
-    /* Configure card */
-    link->state |= DEV_CONFIG;
 
     do {
 
@@ -274,7 +247,7 @@ static void avma1cs_config(dev_link_t *link)
        tuple.DesiredTuple = CISTPL_VERS_1;
 
        devname[0] = 0;
-       if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) {
+       if( !first_tuple(link, &tuple, &parse) && parse.version_1.ns > 1 ) {
            strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1], 
                        sizeof(devname));
        }
@@ -285,7 +258,7 @@ static void avma1cs_config(dev_link_t *link)
        tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
        tuple.Attributes = 0;
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       i = first_tuple(handle, &tuple, &parse);
+       i = first_tuple(link, &tuple, &parse);
        while (i == CS_SUCCESS) {
            if (cf->io.nwin > 0) {
                link->conf.ConfigIndex = cf->index;
@@ -295,36 +268,36 @@ static void avma1cs_config(dev_link_t *link)
                printk(KERN_INFO "avma1_cs: testing i/o %#x-%#x\n",
                        link->io.BasePort1,
                        link->io.BasePort1+link->io.NumPorts1 - 1);
-               i = pcmcia_request_io(link->handle, &link->io);
+               i = pcmcia_request_io(link, &link->io);
                if (i == CS_SUCCESS) goto found_port;
            }
-           i = next_tuple(handle, &tuple, &parse);
+           i = next_tuple(link, &tuple, &parse);
        }
 
 found_port:
        if (i != CS_SUCCESS) {
-           cs_error(link->handle, RequestIO, i);
+           cs_error(link, RequestIO, i);
            break;
        }
        
        /*
         * allocate an interrupt line
         */
-       i = pcmcia_request_irq(link->handle, &link->irq);
+       i = pcmcia_request_irq(link, &link->irq);
        if (i != CS_SUCCESS) {
-           cs_error(link->handle, RequestIRQ, i);
-           pcmcia_release_io(link->handle, &link->io);
+           cs_error(link, RequestIRQ, i);
+           /* undo */
+           pcmcia_disable_device(link);
            break;
        }
-       
+
        /*
         * configure the PCMCIA socket
         */
-       i = pcmcia_request_configuration(link->handle, &link->conf);
+       i = pcmcia_request_configuration(link, &link->conf);
        if (i != CS_SUCCESS) {
-           cs_error(link->handle, RequestConfiguration, i);
-           pcmcia_release_io(link->handle, &link->io);
-           pcmcia_release_irq(link->handle, &link->irq);
+           cs_error(link, RequestConfiguration, i);
+           pcmcia_disable_device(link);
            break;
        }
 
@@ -336,13 +309,12 @@ found_port:
     strcpy(dev->node.dev_name, "A1");
     dev->node.major = 45;
     dev->node.minor = 0;
-    link->dev = &dev->node;
-    
-    link->state &= ~DEV_CONFIG_PENDING;
+    link->dev_node = &dev->node;
+
     /* If any step failed, release any partially configured state */
     if (i != 0) {
        avma1cs_release(link);
-       return;
+       return -ENODEV;
     }
 
     printk(KERN_NOTICE "avma1_cs: checking at i/o %#x, irq %d\n",
@@ -357,10 +329,11 @@ found_port:
     if (i < 0) {
        printk(KERN_ERR "avma1_cs: failed to initialize AVM A1 PCMCIA %d at i/o %#x\n", i, link->io.BasePort1);
        avma1cs_release(link);
-       return;
+       return -ENODEV;
     }
     dev->node.minor = i;
 
+    return 0;
 } /* avma1cs_config */
 
 /*======================================================================
@@ -371,47 +344,18 @@ found_port:
     
 ======================================================================*/
 
-static void avma1cs_release(dev_link_t *link)
+static void avma1cs_release(struct pcmcia_device *link)
 {
-    local_info_t *local = link->priv;
+       local_info_t *local = link->priv;
 
-    DEBUG(0, "avma1cs_release(0x%p)\n", link);
+       DEBUG(0, "avma1cs_release(0x%p)\n", link);
 
-    /* no unregister function with hisax */
-    HiSax_closecard(local->node.minor);
+       /* now unregister function with hisax */
+       HiSax_closecard(local->node.minor);
 
-    /* Unlink the device chain */
-    link->dev = NULL;
-    
-    /* Don't bother checking to see if these succeed or not */
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
 } /* avma1cs_release */
 
-static int avma1cs_suspend(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int avma1cs_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-       return 0;
-}
-
 
 static struct pcmcia_device_id avma1cs_ids[] = {
        PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN A", 0x95d42008, 0xadc9d4bb),
@@ -425,13 +369,11 @@ static struct pcmcia_driver avma1cs_driver = {
        .drv            = {
                .name   = "avma1_cs",
        },
-       .probe          = avma1cs_attach,
+       .probe          = avma1cs_probe,
        .remove         = avma1cs_detach,
        .id_table       = avma1cs_ids,
-       .suspend        = avma1cs_suspend,
-       .resume         = avma1cs_resume,
 };
+
 /*====================================================================*/
 
 static int __init init_avma1_cs(void)
index 062fb8f..e18e75b 100644 (file)
@@ -94,8 +94,8 @@ module_param(protocol, int, 0);
    handler.
 */
 
-static void elsa_cs_config(dev_link_t *link);
-static void elsa_cs_release(dev_link_t *link);
+static int elsa_cs_config(struct pcmcia_device *link);
+static void elsa_cs_release(struct pcmcia_device *link);
 
 /*
    The attach() and detach() entry points are used to create and destroy
@@ -111,7 +111,7 @@ static void elsa_cs_detach(struct pcmcia_device *p_dev);
    example, ethernet cards, modems).  In other cases, there may be
    many actual or logical devices (SCSI adapters, memory cards with
    multiple partitions).  The dev_node_t structures need to be kept
-   in a linked list starting at the 'dev' field of a dev_link_t
+   in a linked list starting at the 'dev' field of a struct pcmcia_device
    structure.  We allocate them in the card's private data structure,
    because they generally shouldn't be allocated dynamically.
    In this case, we also provide a flag to indicate if a device is
@@ -121,7 +121,7 @@ static void elsa_cs_detach(struct pcmcia_device *p_dev);
 */
 
 typedef struct local_info_t {
-    dev_link_t          link;
+       struct pcmcia_device    *p_dev;
     dev_node_t          node;
     int                 busy;
     int                        cardnr;
@@ -139,9 +139,8 @@ typedef struct local_info_t {
 
 ======================================================================*/
 
-static int elsa_cs_attach(struct pcmcia_device *p_dev)
+static int elsa_cs_probe(struct pcmcia_device *link)
 {
-    dev_link_t *link;
     local_info_t *local;
 
     DEBUG(0, "elsa_cs_attach()\n");
@@ -150,8 +149,11 @@ static int elsa_cs_attach(struct pcmcia_device *p_dev)
     local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
     if (!local) return -ENOMEM;
     memset(local, 0, sizeof(local_info_t));
+
+    local->p_dev = link;
+    link->priv = local;
+
     local->cardnr = -1;
-    link = &local->link; link->priv = local;
 
     /* Interrupt setup */
     link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
@@ -170,16 +172,9 @@ static int elsa_cs_attach(struct pcmcia_device *p_dev)
     link->io.IOAddrLines = 3;
 
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    elsa_cs_config(link);
-
-    return 0;
+    return elsa_cs_config(link);
 } /* elsa_cs_attach */
 
 /*======================================================================
@@ -191,20 +186,16 @@ static int elsa_cs_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void elsa_cs_detach(struct pcmcia_device *p_dev)
+static void elsa_cs_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-    local_info_t *info = link->priv;
+       local_info_t *info = link->priv;
 
-    DEBUG(0, "elsa_cs_detach(0x%p)\n", link);
+       DEBUG(0, "elsa_cs_detach(0x%p)\n", link);
 
-    if (link->state & DEV_CONFIG) {
-           info->busy = 1;
-           elsa_cs_release(link);
-    }
-
-    kfree(info);
+       info->busy = 1;
+       elsa_cs_release(link);
 
+       kfree(info);
 } /* elsa_cs_detach */
 
 /*======================================================================
@@ -214,7 +205,7 @@ static void elsa_cs_detach(struct pcmcia_device *p_dev)
     device available to the system.
 
 ======================================================================*/
-static int get_tuple(client_handle_t handle, tuple_t *tuple,
+static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                      cisparse_t *parse)
 {
     int i = pcmcia_get_tuple_data(handle, tuple);
@@ -222,7 +213,7 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple,
     return pcmcia_parse_tuple(handle, tuple, parse);
 }
 
-static int first_tuple(client_handle_t handle, tuple_t *tuple,
+static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                      cisparse_t *parse)
 {
     int i = pcmcia_get_first_tuple(handle, tuple);
@@ -230,7 +221,7 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple,
     return get_tuple(handle, tuple, parse);
 }
 
-static int next_tuple(client_handle_t handle, tuple_t *tuple,
+static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                      cisparse_t *parse)
 {
     int i = pcmcia_get_next_tuple(handle, tuple);
@@ -238,9 +229,8 @@ static int next_tuple(client_handle_t handle, tuple_t *tuple,
     return get_tuple(handle, tuple, parse);
 }
 
-static void elsa_cs_config(dev_link_t *link)
+static int elsa_cs_config(struct pcmcia_device *link)
 {
-    client_handle_t handle;
     tuple_t tuple;
     cisparse_t parse;
     local_info_t *dev;
@@ -250,7 +240,6 @@ static void elsa_cs_config(dev_link_t *link)
     IsdnCard_t icard;
 
     DEBUG(0, "elsa_config(0x%p)\n", link);
-    handle = link->handle;
     dev = link->priv;
 
     /*
@@ -262,7 +251,7 @@ static void elsa_cs_config(dev_link_t *link)
     tuple.TupleDataMax = 255;
     tuple.TupleOffset = 0;
     tuple.Attributes = 0;
-    i = first_tuple(handle, &tuple, &parse);
+    i = first_tuple(link, &tuple, &parse);
     if (i != CS_SUCCESS) {
         last_fn = ParseTuple;
        goto cs_failed;
@@ -270,32 +259,29 @@ static void elsa_cs_config(dev_link_t *link)
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present = parse.config.rmask[0];
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
     tuple.TupleData = (cisdata_t *)buf;
     tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
     tuple.Attributes = 0;
     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-    i = first_tuple(handle, &tuple, &parse);
+    i = first_tuple(link, &tuple, &parse);
     while (i == CS_SUCCESS) {
         if ( (cf->io.nwin > 0) && cf->io.win[0].base) {
             printk(KERN_INFO "(elsa_cs: looks like the 96 model)\n");
             link->conf.ConfigIndex = cf->index;
             link->io.BasePort1 = cf->io.win[0].base;
-            i = pcmcia_request_io(link->handle, &link->io);
+            i = pcmcia_request_io(link, &link->io);
             if (i == CS_SUCCESS) break;
         } else {
           printk(KERN_INFO "(elsa_cs: looks like the 97 model)\n");
           link->conf.ConfigIndex = cf->index;
           for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) {
             link->io.BasePort1 = j;
-            i = pcmcia_request_io(link->handle, &link->io);
+            i = pcmcia_request_io(link, &link->io);
             if (i == CS_SUCCESS) break;
           }
           break;
         }
-        i = next_tuple(handle, &tuple, &parse);
+        i = next_tuple(link, &tuple, &parse);
     }
 
     if (i != CS_SUCCESS) {
@@ -303,14 +289,14 @@ static void elsa_cs_config(dev_link_t *link)
        goto cs_failed;
     }
 
-    i = pcmcia_request_irq(link->handle, &link->irq);
+    i = pcmcia_request_irq(link, &link->irq);
     if (i != CS_SUCCESS) {
         link->irq.AssignedIRQ = 0;
        last_fn = RequestIRQ;
         goto cs_failed;
     }
 
-    i = pcmcia_request_configuration(link->handle, &link->conf);
+    i = pcmcia_request_configuration(link, &link->conf);
     if (i != CS_SUCCESS) {
       last_fn = RequestConfiguration;
       goto cs_failed;
@@ -321,14 +307,11 @@ static void elsa_cs_config(dev_link_t *link)
     sprintf(dev->node.dev_name, "elsa");
     dev->node.major = dev->node.minor = 0x0;
 
-    link->dev = &dev->node;
+    link->dev_node = &dev->node;
 
     /* Finally, report what we've done */
-    printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
-           dev->node.dev_name, link->conf.ConfigIndex,
-           link->conf.Vcc/10, link->conf.Vcc%10);
-    if (link->conf.Vpp1)
-        printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+    printk(KERN_INFO "%s: index 0x%02x: ",
+           dev->node.dev_name, link->conf.ConfigIndex);
     if (link->conf.Attributes & CONF_ENABLE_IRQ)
         printk(", irq %d", link->irq.AssignedIRQ);
     if (link->io.NumPorts1)
@@ -339,8 +322,6 @@ static void elsa_cs_config(dev_link_t *link)
                link->io.BasePort2+link->io.NumPorts2-1);
     printk("\n");
 
-    link->state &= ~DEV_CONFIG_PENDING;
-
     icard.para[0] = link->irq.AssignedIRQ;
     icard.para[1] = link->io.BasePort1;
     icard.protocol = protocol;
@@ -354,10 +335,11 @@ static void elsa_cs_config(dev_link_t *link)
     } else
        ((local_info_t*)link->priv)->cardnr = i;
 
-    return;
+    return 0;
 cs_failed:
-    cs_error(link->handle, last_fn, i);
+    cs_error(link, last_fn, i);
     elsa_cs_release(link);
+    return -ENODEV;
 } /* elsa_cs_config */
 
 /*======================================================================
@@ -368,7 +350,7 @@ cs_failed:
 
 ======================================================================*/
 
-static void elsa_cs_release(dev_link_t *link)
+static void elsa_cs_release(struct pcmcia_device *link)
 {
     local_info_t *local = link->priv;
 
@@ -380,39 +362,23 @@ static void elsa_cs_release(dev_link_t *link)
            HiSax_closecard(local->cardnr);
        }
     }
-    /* Unlink the device chain */
-    link->dev = NULL;
-
-    /* Don't bother checking to see if these succeed or not */
-    if (link->win)
-        pcmcia_release_window(link->win);
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    link->state &= ~DEV_CONFIG;
+
+    pcmcia_disable_device(link);
 } /* elsa_cs_release */
 
-static int elsa_suspend(struct pcmcia_device *p_dev)
+static int elsa_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        local_info_t *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
         dev->busy = 1;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
 
        return 0;
 }
 
-static int elsa_resume(struct pcmcia_device *p_dev)
+static int elsa_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        local_info_t *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_request_configuration(link->handle, &link->conf);
         dev->busy = 0;
 
        return 0;
@@ -430,7 +396,7 @@ static struct pcmcia_driver elsa_cs_driver = {
        .drv            = {
                .name   = "elsa_cs",
        },
-       .probe          = elsa_cs_attach,
+       .probe          = elsa_cs_probe,
        .remove         = elsa_cs_detach,
        .id_table       = elsa_ids,
        .suspend        = elsa_suspend,
index 6f5213a..9bb18f3 100644 (file)
@@ -95,8 +95,8 @@ module_param(protocol, int, 0);
    event handler. 
 */
 
-static void sedlbauer_config(dev_link_t *link);
-static void sedlbauer_release(dev_link_t *link);
+static int sedlbauer_config(struct pcmcia_device *link);
+static void sedlbauer_release(struct pcmcia_device *link);
 
 /*
    The attach() and detach() entry points are used to create and destroy
@@ -119,7 +119,7 @@ static void sedlbauer_detach(struct pcmcia_device *p_dev);
    example, ethernet cards, modems).  In other cases, there may be
    many actual or logical devices (SCSI adapters, memory cards with
    multiple partitions).  The dev_node_t structures need to be kept
-   in a linked list starting at the 'dev' field of a dev_link_t
+   in a linked list starting at the 'dev' field of a struct pcmcia_device
    structure.  We allocate them in the card's private data structure,
    because they generally shouldn't be allocated dynamically.
 
@@ -130,7 +130,7 @@ static void sedlbauer_detach(struct pcmcia_device *p_dev);
 */
    
 typedef struct local_info_t {
-    dev_link_t         link;
+       struct pcmcia_device    *p_dev;
     dev_node_t         node;
     int                        stop;
     int                        cardnr;
@@ -148,11 +148,10 @@ typedef struct local_info_t {
     
 ======================================================================*/
 
-static int sedlbauer_attach(struct pcmcia_device *p_dev)
+static int sedlbauer_probe(struct pcmcia_device *link)
 {
     local_info_t *local;
-    dev_link_t *link;
-    
+
     DEBUG(0, "sedlbauer_attach()\n");
 
     /* Allocate space for private device-specific data */
@@ -160,8 +159,10 @@ static int sedlbauer_attach(struct pcmcia_device *p_dev)
     if (!local) return -ENOMEM;
     memset(local, 0, sizeof(local_info_t));
     local->cardnr = -1;
-    link = &local->link; link->priv = local;
-    
+
+    local->p_dev = link;
+    link->priv = local;
+
     /* Interrupt setup */
     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
     link->irq.IRQInfo1 = IRQ_LEVEL_ID;
@@ -182,18 +183,10 @@ static int sedlbauer_attach(struct pcmcia_device *p_dev)
     link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
     link->io.IOAddrLines = 3;
 
-
     link->conf.Attributes = 0;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    sedlbauer_config(link);
-
-    return 0;
+    return sedlbauer_config(link);
 } /* sedlbauer_attach */
 
 /*======================================================================
@@ -205,19 +198,15 @@ static int sedlbauer_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void sedlbauer_detach(struct pcmcia_device *p_dev)
+static void sedlbauer_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-
-    DEBUG(0, "sedlbauer_detach(0x%p)\n", link);
+       DEBUG(0, "sedlbauer_detach(0x%p)\n", link);
 
-    if (link->state & DEV_CONFIG) {
-           ((local_info_t *)link->priv)->stop = 1;
-           sedlbauer_release(link);
-    }
+       ((local_info_t *)link->priv)->stop = 1;
+       sedlbauer_release(link);
 
-    /* This points to the parent local_info_t struct */
-    kfree(link->priv);
+       /* This points to the parent local_info_t struct */
+       kfree(link->priv);
 } /* sedlbauer_detach */
 
 /*======================================================================
@@ -230,9 +219,8 @@ static void sedlbauer_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void sedlbauer_config(dev_link_t *link)
+static int sedlbauer_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     local_info_t *dev = link->priv;
     tuple_t tuple;
     cisparse_t parse;
@@ -254,18 +242,13 @@ static void sedlbauer_config(dev_link_t *link)
     tuple.TupleData = buf;
     tuple.TupleDataMax = sizeof(buf);
     tuple.TupleOffset = 0;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present = parse.config.rmask[0];
-    
-    /* Configure card */
-    link->state |= DEV_CONFIG;
 
-    /* Look up the current Vcc */
-    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
-    link->conf.Vcc = conf.Vcc;
+    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &conf));
 
     /*
       In this loop, we scan the CIS for configuration table entries,
@@ -280,12 +263,12 @@ static void sedlbauer_config(dev_link_t *link)
       will only use the CIS to fill in implementation-defined details.
     */
     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     while (1) {
        cistpl_cftable_entry_t dflt = { 0 };
        cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
-       if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+       if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+               pcmcia_parse_tuple(link, &tuple, &parse) != 0)
            goto next_entry;
 
        if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
@@ -309,10 +292,10 @@ static void sedlbauer_config(dev_link_t *link)
        }
            
        if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
-           link->conf.Vpp1 = link->conf.Vpp2 =
+           link->conf.Vpp =
                cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
        else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
-           link->conf.Vpp1 = link->conf.Vpp2 =
+           link->conf.Vpp =
                dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
        
        /* Do we need to allocate an interrupt? */
@@ -339,13 +322,13 @@ static void sedlbauer_config(dev_link_t *link)
                link->io.NumPorts2 = io->win[1].len;
            }
            /* This reserves IO space but doesn't actually enable it */
-           if (pcmcia_request_io(link->handle, &link->io) != 0)
+           if (pcmcia_request_io(link, &link->io) != 0)
                goto next_entry;
        }
 
        /*
          Now set up a common memory window, if needed.  There is room
-         in the dev_link_t structure for one memory window handle,
+         in the struct pcmcia_device structure for one memory window handle,
          but if the base addresses need to be saved, or if multiple
          windows are needed, the info should go in the private data
          structure for this device.
@@ -366,7 +349,7 @@ static void sedlbauer_config(dev_link_t *link)
                 req.Size = 0x1000;
 */
            req.AccessSpeed = 0;
-           if (pcmcia_request_window(&link->handle, &req, &link->win) != 0)
+           if (pcmcia_request_window(&link, &req, &link->win) != 0)
                goto next_entry;
            map.Page = 0; map.CardOffset = mem->win[0].card_addr;
            if (pcmcia_map_mem_page(link->win, &map) != 0)
@@ -374,29 +357,25 @@ static void sedlbauer_config(dev_link_t *link)
        }
        /* If we got this far, we're cool! */
        break;
-       
+
     next_entry:
-/* new in dummy.cs 2001/01/28 MN 
-        if (link->io.NumPorts1)
-           pcmcia_release_io(link->handle, &link->io);
-*/
-       CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+       CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
     }
-    
+
     /*
        Allocate an interrupt line.  Note that this does not assign a
        handler to the interrupt, unless the 'Handler' member of the
        irq structure is initialized.
     */
     if (link->conf.Attributes & CONF_ENABLE_IRQ)
-       CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+       CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
        
     /*
        This actually configures the PCMCIA socket -- setting up
        the I/O windows and the interrupt mapping, and putting the
        card and host interface into "Memory and IO" mode.
     */
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
     /*
       At this point, the dev_node_t structure(s) need to be
@@ -404,14 +383,13 @@ static void sedlbauer_config(dev_link_t *link)
     */
     sprintf(dev->node.dev_name, "sedlbauer");
     dev->node.major = dev->node.minor = 0;
-    link->dev = &dev->node;
+    link->dev_node = &dev->node;
 
     /* Finally, report what we've done */
-    printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
-          dev->node.dev_name, link->conf.ConfigIndex,
-          link->conf.Vcc/10, link->conf.Vcc%10);
-    if (link->conf.Vpp1)
-       printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+    printk(KERN_INFO "%s: index 0x%02x:",
+          dev->node.dev_name, link->conf.ConfigIndex);
+    if (link->conf.Vpp)
+       printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10);
     if (link->conf.Attributes & CONF_ENABLE_IRQ)
        printk(", irq %d", link->irq.AssignedIRQ);
     if (link->io.NumPorts1)
@@ -424,8 +402,6 @@ static void sedlbauer_config(dev_link_t *link)
        printk(", mem 0x%06lx-0x%06lx", req.Base,
               req.Base+req.Size-1);
     printk("\n");
-    
-    link->state &= ~DEV_CONFIG_PENDING;
 
     icard.para[0] = link->irq.AssignedIRQ;
     icard.para[1] = link->io.BasePort1;
@@ -437,14 +413,16 @@ static void sedlbauer_config(dev_link_t *link)
        printk(KERN_ERR "sedlbauer_cs: failed to initialize SEDLBAUER PCMCIA %d at i/o %#x\n",
                last_ret, link->io.BasePort1);
        sedlbauer_release(link);
+       return -ENODEV;
     } else
        ((local_info_t*)link->priv)->cardnr = last_ret;
 
-    return;
+    return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
     sedlbauer_release(link);
+    return -ENODEV;
 
 } /* sedlbauer_config */
 
@@ -456,7 +434,7 @@ cs_failed:
     
 ======================================================================*/
 
-static void sedlbauer_release(dev_link_t *link)
+static void sedlbauer_release(struct pcmcia_device *link)
 {
     local_info_t *local = link->priv;
     DEBUG(0, "sedlbauer_release(0x%p)\n", link);
@@ -467,46 +445,23 @@ static void sedlbauer_release(dev_link_t *link)
            HiSax_closecard(local->cardnr);
        }
     }
-    /* Unlink the device chain */
-    link->dev = NULL;
 
-    /*
-      In a normal driver, additional code may be needed to release
-      other kernel data structures associated with this device. 
-    */
-    
-    /* Don't bother checking to see if these succeed or not */
-    if (link->win)
-       pcmcia_release_window(link->win);
-    pcmcia_release_configuration(link->handle);
-    if (link->io.NumPorts1)
-       pcmcia_release_io(link->handle, &link->io);
-    if (link->irq.AssignedIRQ)
-       pcmcia_release_irq(link->handle, &link->irq);
-    link->state &= ~DEV_CONFIG;
+    pcmcia_disable_device(link);
 } /* sedlbauer_release */
 
-static int sedlbauer_suspend(struct pcmcia_device *p_dev)
+static int sedlbauer_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        local_info_t *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
        dev->stop = 1;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
 
        return 0;
 }
 
-static int sedlbauer_resume(struct pcmcia_device *p_dev)
+static int sedlbauer_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        local_info_t *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_request_configuration(link->handle, &link->conf);
        dev->stop = 0;
 
        return 0;
@@ -530,7 +485,7 @@ static struct pcmcia_driver sedlbauer_driver = {
        .drv            = {
                .name   = "sedlbauer_cs",
        },
-       .probe          = sedlbauer_attach,
+       .probe          = sedlbauer_probe,
        .remove         = sedlbauer_detach,
        .id_table       = sedlbauer_ids,
        .suspend        = sedlbauer_suspend,
index 4e5c14c..afcc2ae 100644 (file)
@@ -75,8 +75,8 @@ module_param(protocol, int, 0);
    handler.
 */
 
-static void teles_cs_config(dev_link_t *link);
-static void teles_cs_release(dev_link_t *link);
+static int teles_cs_config(struct pcmcia_device *link);
+static void teles_cs_release(struct pcmcia_device *link);
 
 /*
    The attach() and detach() entry points are used to create and destroy
@@ -89,10 +89,10 @@ static void teles_detach(struct pcmcia_device *p_dev);
 /*
    A linked list of "instances" of the teles_cs device.  Each actual
    PCMCIA card corresponds to one device instance, and is described
-   by one dev_link_t structure (defined in ds.h).
+   by one struct pcmcia_device structure (defined in ds.h).
 
    You may not want to use a linked list for this -- for example, the
-   memory card driver uses an array of dev_link_t pointers, where minor
+   memory card driver uses an array of struct pcmcia_device pointers, where minor
    device numbers are used to derive the corresponding array index.
 */
 
@@ -102,7 +102,7 @@ static void teles_detach(struct pcmcia_device *p_dev);
    example, ethernet cards, modems).  In other cases, there may be
    many actual or logical devices (SCSI adapters, memory cards with
    multiple partitions).  The dev_node_t structures need to be kept
-   in a linked list starting at the 'dev' field of a dev_link_t
+   in a linked list starting at the 'dev' field of a struct pcmcia_device
    structure.  We allocate them in the card's private data structure,
    because they generally shouldn't be allocated dynamically.
    In this case, we also provide a flag to indicate if a device is
@@ -112,7 +112,7 @@ static void teles_detach(struct pcmcia_device *p_dev);
 */
 
 typedef struct local_info_t {
-    dev_link_t          link;
+       struct pcmcia_device    *p_dev;
     dev_node_t          node;
     int                 busy;
     int                        cardnr;
@@ -130,9 +130,8 @@ typedef struct local_info_t {
 
 ======================================================================*/
 
-static int teles_attach(struct pcmcia_device *p_dev)
+static int teles_probe(struct pcmcia_device *link)
 {
-    dev_link_t *link;
     local_info_t *local;
 
     DEBUG(0, "teles_attach()\n");
@@ -142,7 +141,9 @@ static int teles_attach(struct pcmcia_device *p_dev)
     if (!local) return -ENOMEM;
     memset(local, 0, sizeof(local_info_t));
     local->cardnr = -1;
-    link = &local->link; link->priv = local;
+
+    local->p_dev = link;
+    link->priv = local;
 
     /* Interrupt setup */
     link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
@@ -161,16 +162,9 @@ static int teles_attach(struct pcmcia_device *p_dev)
     link->io.IOAddrLines = 5;
 
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    teles_cs_config(link);
-
-    return 0;
+    return teles_cs_config(link);
 } /* teles_attach */
 
 /*======================================================================
@@ -182,20 +176,16 @@ static int teles_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void teles_detach(struct pcmcia_device *p_dev)
+static void teles_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-    local_info_t *info = link->priv;
-
-    DEBUG(0, "teles_detach(0x%p)\n", link);
+       local_info_t *info = link->priv;
 
-    if (link->state & DEV_CONFIG) {
-           info->busy = 1;
-           teles_cs_release(link);
-    }
+       DEBUG(0, "teles_detach(0x%p)\n", link);
 
-    kfree(info);
+       info->busy = 1;
+       teles_cs_release(link);
 
+       kfree(info);
 } /* teles_detach */
 
 /*======================================================================
@@ -205,7 +195,7 @@ static void teles_detach(struct pcmcia_device *p_dev)
     device available to the system.
 
 ======================================================================*/
-static int get_tuple(client_handle_t handle, tuple_t *tuple,
+static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                      cisparse_t *parse)
 {
     int i = pcmcia_get_tuple_data(handle, tuple);
@@ -213,7 +203,7 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple,
     return pcmcia_parse_tuple(handle, tuple, parse);
 }
 
-static int first_tuple(client_handle_t handle, tuple_t *tuple,
+static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                      cisparse_t *parse)
 {
     int i = pcmcia_get_first_tuple(handle, tuple);
@@ -221,7 +211,7 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple,
     return get_tuple(handle, tuple, parse);
 }
 
-static int next_tuple(client_handle_t handle, tuple_t *tuple,
+static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                      cisparse_t *parse)
 {
     int i = pcmcia_get_next_tuple(handle, tuple);
@@ -229,9 +219,8 @@ static int next_tuple(client_handle_t handle, tuple_t *tuple,
     return get_tuple(handle, tuple, parse);
 }
 
-static void teles_cs_config(dev_link_t *link)
+static int teles_cs_config(struct pcmcia_device *link)
 {
-    client_handle_t handle;
     tuple_t tuple;
     cisparse_t parse;
     local_info_t *dev;
@@ -241,7 +230,6 @@ static void teles_cs_config(dev_link_t *link)
     IsdnCard_t icard;
 
     DEBUG(0, "teles_config(0x%p)\n", link);
-    handle = link->handle;
     dev = link->priv;
 
     /*
@@ -253,7 +241,7 @@ static void teles_cs_config(dev_link_t *link)
     tuple.TupleDataMax = 255;
     tuple.TupleOffset = 0;
     tuple.Attributes = 0;
-    i = first_tuple(handle, &tuple, &parse);
+    i = first_tuple(link, &tuple, &parse);
     if (i != CS_SUCCESS) {
         last_fn = ParseTuple;
        goto cs_failed;
@@ -261,32 +249,29 @@ static void teles_cs_config(dev_link_t *link)
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present = parse.config.rmask[0];
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
     tuple.TupleData = (cisdata_t *)buf;
     tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
     tuple.Attributes = 0;
     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-    i = first_tuple(handle, &tuple, &parse);
+    i = first_tuple(link, &tuple, &parse);
     while (i == CS_SUCCESS) {
         if ( (cf->io.nwin > 0) && cf->io.win[0].base) {
             printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
             link->conf.ConfigIndex = cf->index;
             link->io.BasePort1 = cf->io.win[0].base;
-            i = pcmcia_request_io(link->handle, &link->io);
+            i = pcmcia_request_io(link, &link->io);
             if (i == CS_SUCCESS) break;
         } else {
           printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
           link->conf.ConfigIndex = cf->index;
           for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) {
             link->io.BasePort1 = j;
-            i = pcmcia_request_io(link->handle, &link->io);
+            i = pcmcia_request_io(link, &link->io);
             if (i == CS_SUCCESS) break;
           }
           break;
         }
-        i = next_tuple(handle, &tuple, &parse);
+        i = next_tuple(link, &tuple, &parse);
     }
 
     if (i != CS_SUCCESS) {
@@ -294,14 +279,14 @@ static void teles_cs_config(dev_link_t *link)
        goto cs_failed;
     }
 
-    i = pcmcia_request_irq(link->handle, &link->irq);
+    i = pcmcia_request_irq(link, &link->irq);
     if (i != CS_SUCCESS) {
         link->irq.AssignedIRQ = 0;
        last_fn = RequestIRQ;
         goto cs_failed;
     }
 
-    i = pcmcia_request_configuration(link->handle, &link->conf);
+    i = pcmcia_request_configuration(link, &link->conf);
     if (i != CS_SUCCESS) {
       last_fn = RequestConfiguration;
       goto cs_failed;
@@ -312,14 +297,11 @@ static void teles_cs_config(dev_link_t *link)
     sprintf(dev->node.dev_name, "teles");
     dev->node.major = dev->node.minor = 0x0;
 
-    link->dev = &dev->node;
+    link->dev_node = &dev->node;
 
     /* Finally, report what we've done */
-    printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
-           dev->node.dev_name, link->conf.ConfigIndex,
-           link->conf.Vcc/10, link->conf.Vcc%10);
-    if (link->conf.Vpp1)
-        printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+    printk(KERN_INFO "%s: index 0x%02x:",
+           dev->node.dev_name, link->conf.ConfigIndex);
     if (link->conf.Attributes & CONF_ENABLE_IRQ)
         printk(", irq %d", link->irq.AssignedIRQ);
     if (link->io.NumPorts1)
@@ -330,8 +312,6 @@ static void teles_cs_config(dev_link_t *link)
                link->io.BasePort2+link->io.NumPorts2-1);
     printk("\n");
 
-    link->state &= ~DEV_CONFIG_PENDING;
-
     icard.para[0] = link->irq.AssignedIRQ;
     icard.para[1] = link->io.BasePort1;
     icard.protocol = protocol;
@@ -342,13 +322,16 @@ static void teles_cs_config(dev_link_t *link)
        printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
                i, link->io.BasePort1);
        teles_cs_release(link);
-    } else
-       ((local_info_t*)link->priv)->cardnr = i;
+       return -ENODEV;
+    }
+
+    ((local_info_t*)link->priv)->cardnr = i;
+    return 0;
 
-    return;
 cs_failed:
-    cs_error(link->handle, last_fn, i);
+    cs_error(link, last_fn, i);
     teles_cs_release(link);
+    return -ENODEV;
 } /* teles_cs_config */
 
 /*======================================================================
@@ -359,7 +342,7 @@ cs_failed:
 
 ======================================================================*/
 
-static void teles_cs_release(dev_link_t *link)
+static void teles_cs_release(struct pcmcia_device *link)
 {
     local_info_t *local = link->priv;
 
@@ -371,39 +354,23 @@ static void teles_cs_release(dev_link_t *link)
            HiSax_closecard(local->cardnr);
        }
     }
-    /* Unlink the device chain */
-    link->dev = NULL;
-
-    /* Don't bother checking to see if these succeed or not */
-    if (link->win)
-        pcmcia_release_window(link->win);
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    link->state &= ~DEV_CONFIG;
+
+    pcmcia_disable_device(link);
 } /* teles_cs_release */
 
-static int teles_suspend(struct pcmcia_device *p_dev)
+static int teles_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        local_info_t *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
         dev->busy = 1;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
 
        return 0;
 }
 
-static int teles_resume(struct pcmcia_device *p_dev)
+static int teles_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        local_info_t *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_request_configuration(link->handle, &link->conf);
         dev->busy = 0;
 
        return 0;
@@ -421,7 +388,7 @@ static struct pcmcia_driver teles_cs_driver = {
        .drv            = {
                .name   = "teles_cs",
        },
-       .probe          = teles_attach,
+       .probe          = teles_probe,
        .remove         = teles_detach,
        .id_table       = teles_ids,
        .suspend        = teles_suspend,
index aecd9e0..64fd8e7 100644 (file)
@@ -78,8 +78,7 @@ void dm_put_target_type(struct target_type *t)
        if (--ti->use == 0)
                module_put(ti->tt.module);
 
-       if (ti->use < 0)
-               BUG();
+       BUG_ON(ti->use < 0);
        up_read(&_lock);
 
        return;
index 9b374c9..6081941 100644 (file)
@@ -1558,8 +1558,7 @@ static int init_resync(conf_t *conf)
        int buffs;
 
        buffs = RESYNC_WINDOW / RESYNC_BLOCK_SIZE;
-       if (conf->r1buf_pool)
-               BUG();
+       BUG_ON(conf->r1buf_pool);
        conf->r1buf_pool = mempool_create(buffs, r1buf_pool_alloc, r1buf_pool_free,
                                          conf->poolinfo);
        if (!conf->r1buf_pool)
@@ -1732,8 +1731,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
                            !conf->fullsync &&
                            !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
                                break;
-                       if (sync_blocks < (PAGE_SIZE>>9))
-                               BUG();
+                       BUG_ON(sync_blocks < (PAGE_SIZE>>9));
                        if (len > (sync_blocks<<9))
                                len = sync_blocks<<9;
                }
index ab90a6d..617012b 100644 (file)
@@ -1117,8 +1117,7 @@ static int end_sync_read(struct bio *bio, unsigned int bytes_done, int error)
        for (i=0; i<conf->copies; i++)
                if (r10_bio->devs[i].bio == bio)
                        break;
-       if (i == conf->copies)
-               BUG();
+       BUG_ON(i == conf->copies);
        update_head_pos(i, r10_bio);
        d = r10_bio->devs[i].devnum;
 
@@ -1518,8 +1517,7 @@ static int init_resync(conf_t *conf)
        int buffs;
 
        buffs = RESYNC_WINDOW / RESYNC_BLOCK_SIZE;
-       if (conf->r10buf_pool)
-               BUG();
+       BUG_ON(conf->r10buf_pool);
        conf->r10buf_pool = mempool_create(buffs, r10buf_pool_alloc, r10buf_pool_free, conf);
        if (!conf->r10buf_pool)
                return -ENOMEM;
index dae740a..3184360 100644 (file)
@@ -73,10 +73,8 @@ static void print_raid5_conf (raid5_conf_t *conf);
 static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh)
 {
        if (atomic_dec_and_test(&sh->count)) {
-               if (!list_empty(&sh->lru))
-                       BUG();
-               if (atomic_read(&conf->active_stripes)==0)
-                       BUG();
+               BUG_ON(!list_empty(&sh->lru));
+               BUG_ON(atomic_read(&conf->active_stripes)==0);
                if (test_bit(STRIPE_HANDLE, &sh->state)) {
                        if (test_bit(STRIPE_DELAYED, &sh->state))
                                list_add_tail(&sh->lru, &conf->delayed_list);
@@ -184,10 +182,8 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx, int
        raid5_conf_t *conf = sh->raid_conf;
        int i;
 
-       if (atomic_read(&sh->count) != 0)
-               BUG();
-       if (test_bit(STRIPE_HANDLE, &sh->state))
-               BUG();
+       BUG_ON(atomic_read(&sh->count) != 0);
+       BUG_ON(test_bit(STRIPE_HANDLE, &sh->state));
        
        CHECK_DEVLOCK();
        PRINTK("init_stripe called, stripe %llu\n", 
@@ -269,8 +265,7 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector
                                init_stripe(sh, sector, pd_idx, disks);
                } else {
                        if (atomic_read(&sh->count)) {
-                               if (!list_empty(&sh->lru))
-                                       BUG();
+                         BUG_ON(!list_empty(&sh->lru));
                        } else {
                                if (!test_bit(STRIPE_HANDLE, &sh->state))
                                        atomic_inc(&conf->active_stripes);
@@ -465,8 +460,7 @@ static int drop_one_stripe(raid5_conf_t *conf)
        spin_unlock_irq(&conf->device_lock);
        if (!sh)
                return 0;
-       if (atomic_read(&sh->count))
-               BUG();
+       BUG_ON(atomic_read(&sh->count));
        shrink_buffers(sh, conf->pool_size);
        kmem_cache_free(conf->slab_cache, sh);
        atomic_dec(&conf->active_stripes);
@@ -882,8 +876,7 @@ static void compute_parity(struct stripe_head *sh, int method)
        ptr[0] = page_address(sh->dev[pd_idx].page);
        switch(method) {
        case READ_MODIFY_WRITE:
-               if (!test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags))
-                       BUG();
+               BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags));
                for (i=disks ; i-- ;) {
                        if (i==pd_idx)
                                continue;
@@ -896,7 +889,7 @@ static void compute_parity(struct stripe_head *sh, int method)
                                if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
                                        wake_up(&conf->wait_for_overlap);
 
-                               if (sh->dev[i].written) BUG();
+                               BUG_ON(sh->dev[i].written);
                                sh->dev[i].written = chosen;
                                check_xor();
                        }
@@ -912,7 +905,7 @@ static void compute_parity(struct stripe_head *sh, int method)
                                if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
                                        wake_up(&conf->wait_for_overlap);
 
-                               if (sh->dev[i].written) BUG();
+                               BUG_ON(sh->dev[i].written);
                                sh->dev[i].written = chosen;
                        }
                break;
@@ -995,8 +988,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
        if (*bip && (*bip)->bi_sector < bi->bi_sector + ((bi->bi_size)>>9))
                goto overlap;
 
-       if (*bip && bi->bi_next && (*bip) != bi->bi_next)
-               BUG();
+       BUG_ON(*bip && bi->bi_next && (*bip) != bi->bi_next);
        if (*bip)
                bi->bi_next = *bip;
        *bip = bi;
@@ -1430,8 +1422,7 @@ static void handle_stripe(struct stripe_head *sh)
                set_bit(STRIPE_HANDLE, &sh->state);
                if (failed == 0) {
                        char *pagea;
-                       if (uptodate != disks)
-                               BUG();
+                       BUG_ON(uptodate != disks);
                        compute_parity(sh, CHECK_PARITY);
                        uptodate--;
                        pagea = page_address(sh->dev[sh->pd_idx].page);
@@ -2096,8 +2087,7 @@ static void raid5d (mddev_t *mddev)
 
                list_del_init(first);
                atomic_inc(&sh->count);
-               if (atomic_read(&sh->count)!= 1)
-                       BUG();
+               BUG_ON(atomic_read(&sh->count)!= 1);
                spin_unlock_irq(&conf->device_lock);
                
                handled++;
index ab64b37..bc69355 100644 (file)
@@ -91,10 +91,8 @@ static void print_raid6_conf (raid6_conf_t *conf);
 static void __release_stripe(raid6_conf_t *conf, struct stripe_head *sh)
 {
        if (atomic_dec_and_test(&sh->count)) {
-               if (!list_empty(&sh->lru))
-                       BUG();
-               if (atomic_read(&conf->active_stripes)==0)
-                       BUG();
+               BUG_ON(!list_empty(&sh->lru));
+               BUG_ON(atomic_read(&conf->active_stripes)==0);
                if (test_bit(STRIPE_HANDLE, &sh->state)) {
                        if (test_bit(STRIPE_DELAYED, &sh->state))
                                list_add_tail(&sh->lru, &conf->delayed_list);
@@ -202,10 +200,8 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx)
        raid6_conf_t *conf = sh->raid_conf;
        int disks = conf->raid_disks, i;
 
-       if (atomic_read(&sh->count) != 0)
-               BUG();
-       if (test_bit(STRIPE_HANDLE, &sh->state))
-               BUG();
+       BUG_ON(atomic_read(&sh->count) != 0);
+       BUG_ON(test_bit(STRIPE_HANDLE, &sh->state));
 
        CHECK_DEVLOCK();
        PRINTK("init_stripe called, stripe %llu\n",
@@ -284,13 +280,11 @@ static struct stripe_head *get_active_stripe(raid6_conf_t *conf, sector_t sector
                                init_stripe(sh, sector, pd_idx);
                } else {
                        if (atomic_read(&sh->count)) {
-                               if (!list_empty(&sh->lru))
-                                       BUG();
+                               BUG_ON(!list_empty(&sh->lru));
                        } else {
                                if (!test_bit(STRIPE_HANDLE, &sh->state))
                                        atomic_inc(&conf->active_stripes);
-                               if (list_empty(&sh->lru))
-                                       BUG();
+                               BUG_ON(list_empty(&sh->lru));
                                list_del_init(&sh->lru);
                        }
                }
@@ -353,8 +347,7 @@ static int drop_one_stripe(raid6_conf_t *conf)
        spin_unlock_irq(&conf->device_lock);
        if (!sh)
                return 0;
-       if (atomic_read(&sh->count))
-               BUG();
+       BUG_ON(atomic_read(&sh->count));
        shrink_buffers(sh, conf->raid_disks);
        kmem_cache_free(conf->slab_cache, sh);
        atomic_dec(&conf->active_stripes);
@@ -780,7 +773,7 @@ static void compute_parity(struct stripe_head *sh, int method)
                                if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
                                        wake_up(&conf->wait_for_overlap);
 
-                               if (sh->dev[i].written) BUG();
+                               BUG_ON(sh->dev[i].written);
                                sh->dev[i].written = chosen;
                        }
                break;
@@ -970,8 +963,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
        if (*bip && (*bip)->bi_sector < bi->bi_sector + ((bi->bi_size)>>9))
                goto overlap;
 
-       if (*bip && bi->bi_next && (*bip) != bi->bi_next)
-               BUG();
+       BUG_ON(*bip && bi->bi_next && (*bip) != bi->bi_next);
        if (*bip)
                bi->bi_next = *bip;
        *bip = bi;
@@ -1906,8 +1898,7 @@ static void raid6d (mddev_t *mddev)
 
                list_del_init(first);
                atomic_inc(&sh->count);
-               if (atomic_read(&sh->count)!= 1)
-                       BUG();
+               BUG_ON(atomic_read(&sh->count)!= 1);
                spin_unlock_irq(&conf->device_lock);
 
                handled++;
index baa9f58..fffc711 100644 (file)
@@ -51,18 +51,18 @@ config VIDEO_TVEEPROM
        tristate
 
 config USB_DABUSB
-        tristate "DABUSB driver"
-        depends on USB
-        ---help---
-          A Digital Audio Broadcasting (DAB) Receiver for USB and Linux
-          brought to you by the DAB-Team
-          <http://wwwbode.cs.tum.edu/Par/arch/dab/>.  This driver can be taken
-          as an example for URB-based bulk, control, and isochronous
-          transactions. URB's are explained in
-          <Documentation/usb/URB.txt>.
-
-          To compile this driver as a module, choose M here: the
-          module will be called dabusb.
+       tristate "DABUSB driver"
+       depends on USB
+       ---help---
+         A Digital Audio Broadcasting (DAB) Receiver for USB and Linux
+         brought to you by the DAB-Team
+         <http://wwwbode.cs.tum.edu/Par/arch/dab/>.  This driver can be taken
+         as an example for URB-based bulk, control, and isochronous
+         transactions. URB's are explained in
+         <Documentation/usb/URB.txt>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called dabusb.
 
 endmenu
 
index 2337b41..376ca48 100644 (file)
@@ -7,6 +7,7 @@ config DVB_BT8XX
        select DVB_CX24110
        select DVB_OR51211
        select DVB_LGDT330X
+       select FW_LOADER
        help
          Support for PCI cards based on the Bt8xx PCI bridge. Examples are
          the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards,
index 09e96e9..04578df 100644 (file)
@@ -141,12 +141,18 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
        }
 
        if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
-               void *mem = vmalloc(DVR_BUFFER_SIZE);
+               void *mem;
+               if (!dvbdev->readers) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -EBUSY;
+               }
+               mem = vmalloc(DVR_BUFFER_SIZE);
                if (!mem) {
                        mutex_unlock(&dmxdev->mutex);
                        return -ENOMEM;
                }
                dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
+               dvbdev->readers--;
        }
 
        if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
@@ -184,6 +190,7 @@ static int dvb_dvr_release(struct inode *inode, struct file *file)
                                                dmxdev->dvr_orig_fe);
        }
        if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+               dvbdev->readers++;
                if (dmxdev->dvr_buffer.data) {
                        void *mem = dmxdev->dvr_buffer.data;
                        mb();
@@ -1029,8 +1036,7 @@ static struct file_operations dvb_dvr_fops = {
 
 static struct dvb_device dvbdev_dvr = {
        .priv = NULL,
-       .users = 1,
-       .writers = 1,
+       .readers = 1,
        .fops = &dvb_dvr_fops
 };
 
index 2c3ea8f..4f8f257 100644 (file)
@@ -105,6 +105,7 @@ struct dvb_frontend_private {
        fe_status_t status;
        unsigned long tune_mode_flags;
        unsigned int delay;
+       unsigned int reinitialise;
 
        /* swzigzag values */
        unsigned int state;
@@ -121,6 +122,7 @@ struct dvb_frontend_private {
        unsigned int check_wrapped;
 };
 
+static void dvb_frontend_wakeup(struct dvb_frontend *fe);
 
 static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
 {
@@ -213,6 +215,15 @@ static void dvb_frontend_init(struct dvb_frontend *fe)
                fe->ops->init(fe);
 }
 
+void dvb_frontend_reinitialise(struct dvb_frontend *fe)
+{
+       struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+       fepriv->reinitialise = 1;
+       dvb_frontend_wakeup(fe);
+}
+EXPORT_SYMBOL(dvb_frontend_reinitialise);
+
 static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked)
 {
        int q2;
@@ -505,8 +516,8 @@ static int dvb_frontend_thread(void *data)
        fepriv->quality = 0;
        fepriv->delay = 3*HZ;
        fepriv->status = 0;
-       dvb_frontend_init(fe);
        fepriv->wakeup = 0;
+       fepriv->reinitialise = 1;
 
        while (1) {
                up(&fepriv->sem);           /* is locked when we enter the thread... */
@@ -524,6 +535,11 @@ static int dvb_frontend_thread(void *data)
                if (down_interruptible(&fepriv->sem))
                        break;
 
+               if (fepriv->reinitialise) {
+                       dvb_frontend_init(fe);
+                       fepriv->reinitialise = 0;
+               }
+
                /* do an iteration of the tuning loop */
                if (fe->ops->tune) {
                        /* have we been asked to retune? */
index d5aee5a..5926a3b 100644 (file)
@@ -112,6 +112,8 @@ extern int dvb_register_frontend(struct dvb_adapter* dvb,
 
 extern int dvb_unregister_frontend(struct dvb_frontend* fe);
 
+extern void dvb_frontend_reinitialise(struct dvb_frontend *fe);
+
 extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec);
 extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime);
 
index a14e737..7edd636 100644 (file)
@@ -233,6 +233,45 @@ static struct dvb_usb_rc_key dvico_mce_rc_keys[] = {
        { 0xfe, 0x4e, KEY_POWER },
 };
 
+static struct dvb_usb_rc_key dvico_portable_rc_keys[] = {
+       { 0xfc, 0x02, KEY_SETUP },       /* Profile */
+       { 0xfc, 0x43, KEY_POWER2 },
+       { 0xfc, 0x06, KEY_EPG },
+       { 0xfc, 0x5a, KEY_BACK },
+       { 0xfc, 0x05, KEY_MENU },
+       { 0xfc, 0x47, KEY_INFO },
+       { 0xfc, 0x01, KEY_TAB },
+       { 0xfc, 0x42, KEY_PREVIOUSSONG },/* Replay */
+       { 0xfc, 0x49, KEY_VOLUMEUP },
+       { 0xfc, 0x09, KEY_VOLUMEDOWN },
+       { 0xfc, 0x54, KEY_CHANNELUP },
+       { 0xfc, 0x0b, KEY_CHANNELDOWN },
+       { 0xfc, 0x16, KEY_CAMERA },
+       { 0xfc, 0x40, KEY_TUNER },      /* ATV/DTV */
+       { 0xfc, 0x45, KEY_OPEN },
+       { 0xfc, 0x19, KEY_1 },
+       { 0xfc, 0x18, KEY_2 },
+       { 0xfc, 0x1b, KEY_3 },
+       { 0xfc, 0x1a, KEY_4 },
+       { 0xfc, 0x58, KEY_5 },
+       { 0xfc, 0x59, KEY_6 },
+       { 0xfc, 0x15, KEY_7 },
+       { 0xfc, 0x14, KEY_8 },
+       { 0xfc, 0x17, KEY_9 },
+       { 0xfc, 0x44, KEY_ANGLE },      /* Aspect */
+       { 0xfc, 0x55, KEY_0 },
+       { 0xfc, 0x07, KEY_ZOOM },
+       { 0xfc, 0x0a, KEY_REWIND },
+       { 0xfc, 0x08, KEY_PLAYPAUSE },
+       { 0xfc, 0x4b, KEY_FASTFORWARD },
+       { 0xfc, 0x5b, KEY_MUTE },
+       { 0xfc, 0x04, KEY_STOP },
+       { 0xfc, 0x56, KEY_RECORD },
+       { 0xfc, 0x57, KEY_POWER },
+       { 0xfc, 0x41, KEY_UNKNOWN },    /* INPUT */
+       { 0xfc, 0x00, KEY_UNKNOWN },    /* HD */
+};
+
 static int cxusb_dee1601_demod_init(struct dvb_frontend* fe)
 {
        static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x28 };
@@ -511,6 +550,11 @@ static struct dvb_usb_properties cxusb_bluebird_lgh064f_properties = {
 
        .i2c_algo         = &cxusb_i2c_algo,
 
+       .rc_interval      = 100,
+       .rc_key_map       = dvico_portable_rc_keys,
+       .rc_key_map_size  = ARRAY_SIZE(dvico_portable_rc_keys),
+       .rc_query         = cxusb_rc_query,
+
        .generic_bulk_ctrl_endpoint = 0x01,
        /* parameter for the MPEG2-data transfer */
        .urb = {
@@ -600,6 +644,11 @@ static struct dvb_usb_properties cxusb_bluebird_lgz201_properties = {
 
        .i2c_algo         = &cxusb_i2c_algo,
 
+       .rc_interval      = 100,
+       .rc_key_map       = dvico_portable_rc_keys,
+       .rc_key_map_size  = ARRAY_SIZE(dvico_portable_rc_keys),
+       .rc_query         = cxusb_rc_query,
+
        .generic_bulk_ctrl_endpoint = 0x01,
        /* parameter for the MPEG2-data transfer */
        .urb = {
@@ -640,6 +689,11 @@ static struct dvb_usb_properties cxusb_bluebird_dtt7579_properties = {
 
        .i2c_algo         = &cxusb_i2c_algo,
 
+       .rc_interval      = 100,
+       .rc_key_map       = dvico_portable_rc_keys,
+       .rc_key_map_size  = ARRAY_SIZE(dvico_portable_rc_keys),
+       .rc_query         = cxusb_rc_query,
+
        .generic_bulk_ctrl_endpoint = 0x01,
        /* parameter for the MPEG2-data transfer */
        .urb = {
index 12ebaf8..70afcfd 100644 (file)
@@ -94,12 +94,14 @@ static int dtt200u_frontend_attach(struct dvb_usb_device *d)
 
 static struct dvb_usb_properties dtt200u_properties;
 static struct dvb_usb_properties wt220u_properties;
+static struct dvb_usb_properties wt220u_zl0353_properties;
 
 static int dtt200u_usb_probe(struct usb_interface *intf,
                const struct usb_device_id *id)
 {
        if (dvb_usb_device_init(intf,&dtt200u_properties,THIS_MODULE,NULL) == 0 ||
-               dvb_usb_device_init(intf,&wt220u_properties,THIS_MODULE,NULL) == 0)
+               dvb_usb_device_init(intf,&wt220u_properties,THIS_MODULE,NULL) == 0 ||
+               dvb_usb_device_init(intf,&wt220u_zl0353_properties,THIS_MODULE,NULL) == 0)
                return 0;
 
        return -ENODEV;
@@ -110,6 +112,8 @@ static struct usb_device_id dtt200u_usb_table [] = {
        { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_WARM) },
        { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_COLD)  },
        { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_WARM)  },
+       { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_COLD)  },
+       { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_WARM)  },
        { 0 },
 };
 MODULE_DEVICE_TABLE(usb, dtt200u_usb_table);
@@ -196,6 +200,47 @@ static struct dvb_usb_properties wt220u_properties = {
        }
 };
 
+static struct dvb_usb_properties wt220u_zl0353_properties = {
+       .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING,
+       .pid_filter_count = 15,
+
+       .usb_ctrl = CYPRESS_FX2,
+       .firmware = "dvb-usb-wt220u-zl0353-01.fw",
+
+       .power_ctrl      = dtt200u_power_ctrl,
+       .streaming_ctrl  = dtt200u_streaming_ctrl,
+       .pid_filter      = dtt200u_pid_filter,
+       .frontend_attach = dtt200u_frontend_attach,
+
+       .rc_interval     = 300,
+       .rc_key_map      = dtt200u_rc_keys,
+       .rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys),
+       .rc_query        = dtt200u_rc_query,
+
+       .generic_bulk_ctrl_endpoint = 0x01,
+
+       /* parameter for the MPEG2-data transfer */
+       .urb = {
+               .type = DVB_USB_BULK,
+               .count = 7,
+               .endpoint = 0x02,
+               .u = {
+                       .bulk = {
+                               .buffersize = 4096,
+                       }
+               }
+       },
+
+       .num_device_descs = 1,
+       .devices = {
+               { .name = "WideView WT-220U PenType Receiver (based on ZL353)",
+                 .cold_ids = { &dtt200u_usb_table[4], NULL },
+                 .warm_ids = { &dtt200u_usb_table[5], NULL },
+               },
+               { NULL },
+       }
+};
+
 /* usb specific object needed to register this driver with the usb subsystem */
 static struct usb_driver dtt200u_usb_driver = {
        .name           = "dvb_usb_dtt200u",
index 4a1b9e7..cb23904 100644 (file)
@@ -83,6 +83,8 @@
 #define USB_PID_DTT200U_WARM                           0x0301
 #define USB_PID_WT220U_COLD                                    0x0222
 #define USB_PID_WT220U_WARM                                    0x0221
+#define USB_PID_WT220U_ZL0353_COLD                     0x022a
+#define USB_PID_WT220U_ZL0353_WARM                     0x022b
 #define USB_PID_WINTV_NOVA_T_USB2_COLD         0x9300
 #define USB_PID_WINTV_NOVA_T_USB2_WARM         0x9301
 #define USB_PID_NEBULA_DIGITV                          0x0201
index b6d95e1..2a89f8c 100644 (file)
@@ -147,8 +147,9 @@ static int vp702x_fe_set_frontend(struct dvb_frontend* fe,
        cmd[4] = (sr >> 4)  & 0xff;
        cmd[5] = (sr << 4)  & 0xf0;
 
-       deb_fe("setting frontend to: %u -> %u (%x) LNB-based GHz, symbolrate: %d -> %Lu (%Lx)\n",
-                       fep->frequency,freq,freq, fep->u.qpsk.symbol_rate, sr, sr);
+       deb_fe("setting frontend to: %u -> %u (%x) LNB-based GHz, symbolrate: %d -> %lu (%lx)\n",
+                       fep->frequency,freq,freq, fep->u.qpsk.symbol_rate,
+                       (unsigned long) sr, (unsigned long) sr);
 
 /*     if (fep->inversion == INVERSION_ON)
                cmd[6] |= 0x80; */
index 9423316..37d5e0a 100644 (file)
@@ -181,6 +181,11 @@ config DVB_OR51211
        help
          An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
 
+         This driver needs external firmware. Please use the command
+         "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to
+         download it, and then copy it to /usr/lib/hotplug/firmware
+         or /lib/firmware (depending on configuration of firmware hotplug).
+
 config DVB_OR51132
        tristate "Oren OR51132 based"
        depends on DVB_CORE
@@ -189,6 +194,13 @@ config DVB_OR51132
          An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
          to support this frontend.
 
+         This driver needs external firmware. Please use the commands
+         "<kerneldir>/Documentation/dvb/get_dvb_firmware or51132_vsb" and/or
+         "<kerneldir>/Documentation/dvb/get_dvb_firmware or51132_qam" to
+         download firmwares for 8VSB and QAM64/256, respectively. Copy them to
+         /usr/lib/hotplug/firmware or /lib/firmware (depending on
+         configuration of firmware hotplug).
+
 config DVB_BCM3510
        tristate "Broadcom BCM3510"
        depends on DVB_CORE
index 8e8df7b..b83dafa 100644 (file)
@@ -52,7 +52,6 @@ struct tda1004x_state {
        struct dvb_frontend frontend;
 
        /* private demod data */
-       u8 initialised;
        enum tda1004x_demod demod_type;
 };
 
@@ -594,9 +593,6 @@ static int tda10045_init(struct dvb_frontend* fe)
 
        dprintk("%s\n", __FUNCTION__);
 
-       if (state->initialised)
-               return 0;
-
        if (tda10045_fwupload(fe)) {
                printk("tda1004x: firmware upload failed\n");
                return -EIO;
@@ -626,7 +622,6 @@ static int tda10045_init(struct dvb_frontend* fe)
 
        tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk);
 
-       state->initialised = 1;
        return 0;
 }
 
@@ -635,9 +630,6 @@ static int tda10046_init(struct dvb_frontend* fe)
        struct tda1004x_state* state = fe->demodulator_priv;
        dprintk("%s\n", __FUNCTION__);
 
-       if (state->initialised)
-               return 0;
-
        if (tda10046_fwupload(fe)) {
                printk("tda1004x: firmware upload failed\n");
                        return -EIO;
@@ -697,7 +689,6 @@ static int tda10046_init(struct dvb_frontend* fe)
        // tda1004x_write_mask(state, 0x50, 0x80, 0x80);         // handle out of guard echoes
        tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7);
 
-       state->initialised = 1;
        return 0;
 }
 
@@ -1207,7 +1198,6 @@ static int tda1004x_sleep(struct dvb_frontend* fe)
                tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1);
                break;
        }
-       state->initialised = 0;
 
        return 0;
 }
@@ -1271,7 +1261,6 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
        state->config = config;
        state->i2c = i2c;
        memcpy(&state->ops, &tda10045_ops, sizeof(struct dvb_frontend_ops));
-       state->initialised = 0;
        state->demod_type = TDA1004X_DEMOD_TDA10045;
 
        /* check if the demod is there */
@@ -1330,7 +1319,6 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
        state->config = config;
        state->i2c = i2c;
        memcpy(&state->ops, &tda10046_ops, sizeof(struct dvb_frontend_ops));
-       state->initialised = 0;
        state->demod_type = TDA1004X_DEMOD_TDA10046;
 
        /* check if the demod is there */
index 840efec..d028245 100644 (file)
@@ -87,6 +87,7 @@ static int volume = 255;
 static int budgetpatch;
 static int wss_cfg_4_3 = 0x4008;
 static int wss_cfg_16_9 = 0x0007;
+static int tv_standard;
 
 module_param_named(debug, av7110_debug, int, 0644);
 MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)");
@@ -109,6 +110,8 @@ module_param(wss_cfg_4_3, int, 0444);
 MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data");
 module_param(wss_cfg_16_9, int, 0444);
 MODULE_PARM_DESC(wss_cfg_16_9, "WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data");
+module_param(tv_standard, int, 0444);
+MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC");
 
 static void restart_feeds(struct av7110 *av7110);
 
@@ -2123,7 +2126,7 @@ static int frontend_init(struct av7110 *av7110)
                                                        read_pwm(av7110));
                                break;
                        case 0x0003:
-                               /* Haupauge DVB-C 2.1 VES1820/ALPS TDBE2 */
+                               /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */
                                av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap,
                                                        read_pwm(av7110));
                                break;
@@ -2543,6 +2546,9 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
        av7110->osdwin = 1;
        mutex_init(&av7110->osd_mutex);
 
+       /* TV standard */
+       av7110->vidmode = tv_standard == 1 ? VIDEO_MODE_NTSC : VIDEO_MODE_PAL;
+
        /* ARM "watchdog" */
        init_waitqueue_head(&av7110->arm_wait);
        av7110->arm_thread = NULL;
index 400face..2eff09f 100644 (file)
@@ -1479,8 +1479,6 @@ int av7110_av_init(struct av7110 *av7110)
        void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb };
        int i, ret;
 
-       av7110->vidmode = VIDEO_MODE_PAL;
-
        for (i = 0; i < 2; i++) {
                struct ipack *ipack = av7110->ipack + i;
 
index 9dd4745..8efe3ce 100644 (file)
@@ -60,11 +60,11 @@ struct budget_av {
        struct dvb_ca_en50221 ca;
 };
 
-/* GPIO CI Connections:
- * 0 - Vcc/Reset (Reset is controlled by capacitor)
- * 1 - Attribute Memory
- * 2 - Card Enable (Active Low)
- * 3 - Card Detect
+/* GPIO Connections:
+ * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*!
+ * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory
+ * 2 - CI Card Enable (Active Low)
+ * 3 - CI Card Detect
  */
 
 /****************************************************************************
@@ -214,6 +214,9 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
        while (--timeout > 0 && ciintf_read_attribute_mem(ca, slot, 0) != 0x1d)
                msleep(100);
 
+       /* reinitialise the frontend */
+       dvb_frontend_reinitialise(budget_av->budget.dvb_frontend);
+
        if (timeout <= 0)
        {
                printk(KERN_ERR "budget-av: cam reset failed (timeout).\n");
index 633e68c..ea2066d 100644 (file)
 #include "budget.h"
 #include "ttpci-eeprom.h"
 
+#define TS_WIDTH               (2 * TS_SIZE)
+#define TS_WIDTH_ACTIVY                TS_SIZE
+#define TS_HEIGHT_MASK         0xf00
+#define TS_HEIGHT_MASK_ACTIVY  0xc00
+#define TS_MIN_BUFSIZE_K       188
+#define TS_MAX_BUFSIZE_K       1410
+#define TS_MAX_BUFSIZE_K_ACTIVY        564
+#define BUFFER_WARNING_WAIT    (30*HZ)
+
 int budget_debug;
+static int dma_buffer_size = TS_MIN_BUFSIZE_K;
 module_param_named(debug, budget_debug, int, 0644);
+module_param_named(bufsize, dma_buffer_size, int, 0444);
 MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off).");
+MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)");
 
 /****************************************************************************
  * TT budget / WinTV Nova
@@ -70,11 +82,10 @@ static int start_ts_capture(struct budget *budget)
 
        saa7146_write(dev, MC1, MASK_20);       // DMA3 off
 
-       memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH);
+       memset(budget->grabbing, 0x00, budget->buffer_size);
 
        saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
 
-       budget->tsf = 0xff;
        budget->ttbp = 0;
 
        /*
@@ -115,16 +126,12 @@ static int start_ts_capture(struct budget *budget)
 
        saa7146_write(dev, BASE_ODD3, 0);
        saa7146_write(dev, BASE_EVEN3, 0);
-       saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT);
+       saa7146_write(dev, PROT_ADDR3, budget->buffer_size);
        saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90);
 
-       if (budget->card->type == BUDGET_FS_ACTIVY) {
-               saa7146_write(dev, PITCH3, TS_WIDTH / 2);
-               saa7146_write(dev, NUM_LINE_BYTE3, ((TS_HEIGHT * 2) << 16) | (TS_WIDTH / 2));
-       } else {
-               saa7146_write(dev, PITCH3, TS_WIDTH);
-               saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH);
-       }
+       saa7146_write(dev, PITCH3, budget->buffer_width);
+       saa7146_write(dev, NUM_LINE_BYTE3,
+                       (budget->buffer_height << 16) | budget->buffer_width);
 
        saa7146_write(dev, MC2, (MASK_04 | MASK_20));
 
@@ -141,11 +148,12 @@ static void vpeirq(unsigned long data)
        u8 *mem = (u8 *) (budget->grabbing);
        u32 olddma = budget->ttbp;
        u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+       u32 count;
 
        /* nearest lower position divisible by 188 */
        newdma -= newdma % 188;
 
-       if (newdma >= TS_BUFLEN)
+       if (newdma >= budget->buffer_size)
                return;
 
        budget->ttbp = newdma;
@@ -154,11 +162,24 @@ static void vpeirq(unsigned long data)
                return;
 
        if (newdma > olddma) {  /* no wraparound, dump olddma..newdma */
-               dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (newdma - olddma) / 188);
+               count = newdma - olddma;
+               dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188);
        } else {                /* wraparound, dump olddma..buflen and 0..newdma */
-               dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (TS_BUFLEN - olddma) / 188);
+               count = budget->buffer_size - olddma;
+               dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188);
+               count += newdma;
                dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188);
        }
+
+       if (count > budget->buffer_warning_threshold)
+               budget->buffer_warnings++;
+
+       if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) {
+               printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n",
+                       budget->dev->name, __FUNCTION__, budget->buffer_warnings, count);
+               budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT;
+               budget->buffer_warnings = 0;
+       }
 }
 
 
@@ -341,9 +362,10 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
                      struct saa7146_pci_extension_data *info,
                      struct module *owner)
 {
-       int length = TS_WIDTH * TS_HEIGHT;
        int ret = 0;
        struct budget_info *bi = info->ext_priv;
+       int max_bufsize;
+       int height_mask;
 
        memset(budget, 0, sizeof(struct budget));
 
@@ -352,6 +374,32 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
        budget->card = bi;
        budget->dev = (struct saa7146_dev *) dev;
 
+       if (budget->card->type == BUDGET_FS_ACTIVY) {
+               budget->buffer_width = TS_WIDTH_ACTIVY;
+               max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY;
+               height_mask = TS_HEIGHT_MASK_ACTIVY;
+       } else {
+               budget->buffer_width = TS_WIDTH;
+               max_bufsize = TS_MAX_BUFSIZE_K;
+               height_mask = TS_HEIGHT_MASK;
+       }
+
+       if (dma_buffer_size < TS_MIN_BUFSIZE_K)
+               dma_buffer_size = TS_MIN_BUFSIZE_K;
+       else if (dma_buffer_size > max_bufsize)
+               dma_buffer_size = max_bufsize;
+
+       budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width;
+       budget->buffer_height &= height_mask;
+       budget->buffer_size = budget->buffer_height * budget->buffer_width;
+       budget->buffer_warning_threshold = budget->buffer_size * 80/100;
+       budget->buffer_warnings = 0;
+       budget->buffer_warning_time = jiffies;
+
+       dprintk(2, "%s: width = %d, height = %d\n",
+               budget->dev->name, budget->buffer_width, budget->buffer_height);
+       printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
+
        dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner);
 
        /* set dd1 stream a & b */
@@ -392,7 +440,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
        ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac);
 
        if (NULL ==
-           (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, length, &budget->pt))) {
+           (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt))) {
                ret = -ENOMEM;
                goto err;
        }
index 9fc9185..1b3aaac 100644 (file)
@@ -577,6 +577,17 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte
        saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
        // Set RPS1 Address register to point to RPS code               (r108 p42)
        saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+
+       if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL)))
+               return -ENOMEM;
+
+       dprintk(2, "budget: %p\n", budget);
+
+       if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) {
+               kfree (budget);
+               return err;
+       }
+
        // Set Source Line Counter Threshold, using BRS                 (rCC p43)
        // It generates HS event every TS_HEIGHT lines
        // this is related to TS_WIDTH set in register
@@ -585,24 +596,13 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte
        //,then RPS_THRESH1
        // should be set to trigger every TS_HEIGHT (512) lines.
        //
-       saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 );
+       saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 );
 
        // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 );
        // Enable RPS1                                                  (rFC p33)
        saa7146_write(dev, MC1, (MASK_13 | MASK_29));
 
 
-       if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL)))
-               return -ENOMEM;
-
-       dprintk(2, "budget: %p\n", budget);
-
-       if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) {
-               kfree (budget);
-               return err;
-       }
-
-
        dev->ext_priv = budget;
 
        budget->dvb_adapter.priv = budget;
index 4ac0f4d..ecea3a1 100644 (file)
@@ -58,7 +58,13 @@ struct budget {
        int ci_present;
        int video_port;
 
-       u8 tsf;
+       u32 buffer_width;
+       u32 buffer_height;
+       u32 buffer_size;
+       u32 buffer_warning_threshold;
+       u32 buffer_warnings;
+       unsigned long buffer_warning_time;
+
        u32 ttbp;
        int feeding;
 
@@ -79,11 +85,6 @@ static struct saa7146_pci_extension_data x_var = { \
        .ext_priv = &x_var ## _info, \
        .ext = &budget_extension };
 
-#define TS_WIDTH  (376)
-#define TS_HEIGHT (512)
-#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT)
-#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE)
-
 #define BUDGET_TT                 0
 #define BUDGET_TT_HW_DISEQC       1
 #define BUDGET_PATCH              3
index f31a198..85888a8 100644 (file)
@@ -300,7 +300,7 @@ config VIDEO_OVCAMCHIP
          camera drivers.
 
          To compile this driver as a module, choose M here: the
-         module will be called ovcamchip
+         module will be called ovcamchip.
 
 config VIDEO_M32R_AR
        tristate "AR devices"
@@ -316,6 +316,13 @@ config VIDEO_M32R_AR_M64278
          Say Y here to use the Renesas M64278E-800 camera module,
          which supports VGA(640x480 pixcels) size of images.
 
+#
+# Encoder / Decoder module configuration
+#
+
+menu "Encoders and Decoders"
+       depends on VIDEO_DEV
+
 config VIDEO_MSP3400
        tristate "Micronas MSP34xx audio decoders"
        depends on VIDEO_DEV && I2C
@@ -323,7 +330,7 @@ config VIDEO_MSP3400
          Support for the Micronas MSP34xx series of audio decoders.
 
          To compile this driver as a module, choose M here: the
-         module will be called msp3400
+         module will be called msp3400.
 
 config VIDEO_CS53L32A
        tristate "Cirrus Logic CS53L32A audio ADC"
@@ -333,17 +340,27 @@ config VIDEO_CS53L32A
          stereo A/D converter.
 
          To compile this driver as a module, choose M here: the
-         module will be called cs53l32a
+         module will be called cs53l32a.
 
 config VIDEO_WM8775
-       tristate "Wolfson Microelectronics WM8775 audio ADC"
+       tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer"
        depends on VIDEO_DEV && I2C && EXPERIMENTAL
        ---help---
-         Support for the Wolfson Microelectronics WM8775
-         high performance stereo A/D Converter.
+         Support for the Wolfson Microelectronics WM8775 high
+         performance stereo A/D Converter with a 4 channel input mixer.
 
          To compile this driver as a module, choose M here: the
-         module will be called wm8775
+         module will be called wm8775.
+
+config VIDEO_WM8739
+       tristate "Wolfson Microelectronics WM8739 stereo audio ADC"
+       depends on VIDEO_DEV && I2C && EXPERIMENTAL
+       ---help---
+         Support for the Wolfson Microelectronics WM8739
+         stereo A/D Converter.
+
+         To compile this driver as a module, choose M here: the
+         module will be called wm8739.
 
 source "drivers/media/video/cx25840/Kconfig"
 
@@ -354,7 +371,7 @@ config VIDEO_SAA711X
          Support for the Philips SAA7113/4/5 video decoders.
 
          To compile this driver as a module, choose M here: the
-         module will be called saa7115
+         module will be called saa7115.
 
 config VIDEO_SAA7127
        tristate "Philips SAA7127/9 digital video encoders"
@@ -363,7 +380,32 @@ config VIDEO_SAA7127
          Support for the Philips SAA7127/9 digital video encoders.
 
          To compile this driver as a module, choose M here: the
-         module will be called saa7127
+         module will be called saa7127.
+
+config VIDEO_UPD64031A
+       tristate "NEC Electronics uPD64031A Ghost Reduction"
+       depends on VIDEO_DEV && I2C && EXPERIMENTAL
+       ---help---
+         Support for the NEC Electronics uPD64031A Ghost Reduction
+         video chip. It is most often found in NTSC TV cards made for
+         Japan and is used to reduce the 'ghosting' effect that can
+         be present in analog TV broadcasts.
+
+         To compile this driver as a module, choose M here: the
+         module will be called upd64031a.
+
+config VIDEO_UPD64083
+       tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation"
+       depends on VIDEO_DEV && I2C && EXPERIMENTAL
+       ---help---
+         Support for the NEC Electronics uPD64083 3-Dimensional Y/C
+         separation video chip. It is used to improve the quality of
+         the colors of a composite signal.
+
+         To compile this driver as a module, choose M here: the
+         module will be called upd64083.
+
+endmenu # encoder / decoder chips
 
 #
 # USB Multimedia device configuration
@@ -374,20 +416,6 @@ menu "V4L USB devices"
 
 source "drivers/media/video/em28xx/Kconfig"
 
-config USB_VICAM
-       tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)"
-       depends on USB && VIDEO_DEV && EXPERIMENTAL
-       ---help---
-         Say Y here if you have 3com homeconnect camera (vicam).
-
-         This driver uses the Video For Linux API. You must say Y or M to
-         "Video For Linux" (under Multimedia Devices) to use this driver.
-         Information on this API and pointers to "v4l" programs may be found
-         at <file:Documentation/video4linux/API.html>.
-
-         To compile this driver as a module, choose M here: the
-         module will be called vicam.
-
 config USB_DSBR
        tristate "D-Link USB FM radio support (EXPERIMENTAL)"
        depends on USB && VIDEO_DEV && EXPERIMENTAL
@@ -397,79 +425,20 @@ config USB_DSBR
          you must connect the line out connector to a sound card or a
          set of speakers.
 
-         This driver uses the Video For Linux API.  You must enable
-         (Y or M in config) Video For Linux (under Character Devices)
-         to use this driver.  Information on this API and pointers to
-         "v4l" programs may be found at
-         <file:Documentation/video4linux/API.html>.
-
          To compile this driver as a module, choose M here: the
          module will be called dsbr100.
 
-config USB_ET61X251
-       tristate "USB ET61X[12]51 PC Camera Controller support"
-       depends on USB && VIDEO_DEV
-       ---help---
-         Say Y here if you want support for cameras based on Etoms ET61X151
-         or ET61X251 PC Camera Controllers.
-
-         See <file:Documentation/usb/et61x251.txt> for more informations.
-
-         This driver uses the Video For Linux API. You must say Y or M to
-         "Video For Linux" to use this driver.
-
-         To compile this driver as a module, choose M here: the
-         module will be called et61x251.
+source "drivers/media/video/usbvideo/Kconfig"
 
-config USB_IBMCAM
-       tristate "USB IBM (Xirlink) C-it Camera support"
-       depends on USB && VIDEO_DEV
-       ---help---
-         Say Y here if you want to connect a IBM "C-It" camera, also known as
-         "Xirlink PC Camera" to your computer's USB port.  For more
-         information, read <file:Documentation/usb/ibmcam.txt>.
-
-         This driver uses the Video For Linux API.  You must enable
-         (Y or M in config) Video For Linux (under Character Devices)
-         to use this driver.  Information on this API and pointers to
-         "v4l" programs may be found at
-         <file:Documentation/video4linux/API.html>.
-
-         To compile this driver as a module, choose M here: the
-         module will be called ibmcam.
-
-         This camera has several configuration options which
-         can be specified when you load the module. Read
-         <file:Documentation/usb/ibmcam.txt> to learn more.
-
-config USB_KONICAWC
-       tristate "USB Konica Webcam support"
-       depends on USB && VIDEO_DEV
-       ---help---
-         Say Y here if you want support for webcams based on a Konica
-         chipset. This is known to work with the Intel YC76 webcam.
-
-         This driver uses the Video For Linux API.  You must enable
-         (Y or M in config) Video For Linux (under Character Devices)
-         to use this driver.  Information on this API and pointers to
-         "v4l" programs may be found at
-         <file:Documentation/video4linux/API.html>.
-
-         To compile this driver as a module, choose M here: the
-         module will be called konicawc.
+source "drivers/media/video/et61x251/Kconfig"
 
 config USB_OV511
        tristate "USB OV511 Camera support"
        depends on USB && VIDEO_DEV
        ---help---
          Say Y here if you want to connect this type of camera to your
-         computer's USB port. See <file:Documentation/usb/ov511.txt> for more
-         information and for a list of supported cameras.
-
-         This driver uses the Video For Linux API. You must say Y or M to
-         "Video For Linux" (under Character Devices) to use this driver.
-         Information on this API and pointers to "v4l" programs may be found
-         at <file:Documentation/video4linux/API.html>.
+         computer's USB port. See <file:Documentation/video4linux/ov511.txt>
+         for more information and for a list of supported cameras.
 
          To compile this driver as a module, choose M here: the
          module will be called ov511.
@@ -479,31 +448,13 @@ config USB_SE401
        depends on USB && VIDEO_DEV
        ---help---
          Say Y here if you want to connect this type of camera to your
-         computer's USB port. See <file:Documentation/usb/se401.txt> for more
-         information and for a list of supported cameras.
-
-         This driver uses the Video For Linux API. You must say Y or M to
-         "Video For Linux" (under Multimedia Devices) to use this driver.
-         Information on this API and pointers to "v4l" programs may be found
-         at <file:Documentation/video4linux/API.html>.
+         computer's USB port. See <file:Documentation/video4linux/se401.txt>
+         for more information and for a list of supported cameras.
 
          To compile this driver as a module, choose M here: the
          module will be called se401.
 
-config USB_SN9C102
-       tristate "USB SN9C10x PC Camera Controller support"
-       depends on USB && VIDEO_DEV
-       ---help---
-         Say Y here if you want support for cameras based on SONiX SN9C101,
-         SN9C102 or SN9C103 PC Camera Controllers.
-
-         See <file:Documentation/usb/sn9c102.txt> for more informations.
-
-         This driver uses the Video For Linux API. You must say Y or M to
-         "Video For Linux" to use this driver.
-
-         To compile this driver as a module, choose M here: the
-         module will be called sn9c102.
+source "drivers/media/video/sn9c102/Kconfig"
 
 config USB_STV680
        tristate "USB STV680 (Pencam) Camera support"
@@ -511,20 +462,16 @@ config USB_STV680
        ---help---
          Say Y here if you want to connect this type of camera to your
          computer's USB port. This includes the Pencam line of cameras.
-         See <file:Documentation/usb/stv680.txt> for more information and for
-         a list of supported cameras.
-
-         This driver uses the Video For Linux API. You must say Y or M to
-         "Video For Linux" (under Multimedia Devices) to use this driver.
-         Information on this API and pointers to "v4l" programs may be found
-         at <file:Documentation/video4linux/API.html>.
+         See <file:Documentation/video4linux/stv680.txt> for more information
+         and for a list of supported cameras.
 
          To compile this driver as a module, choose M here: the
          module will be called stv680.
 
 config USB_W9968CF
        tristate "USB W996[87]CF JPEG Dual Mode Camera support"
-       depends on USB && VIDEO_DEV && I2C && VIDEO_OVCAMCHIP
+       depends on USB && VIDEO_DEV && I2C
+       select VIDEO_OVCAMCHIP
        ---help---
          Say Y here if you want support for cameras based on OV681 or
          Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
@@ -534,64 +481,14 @@ config USB_W9968CF
          resolutions and framerates, but cannot be included in the official
          Linux kernel for performance purposes.
 
-         See <file:Documentation/usb/w9968cf.txt> for more informations.
-
-         This driver uses the Video For Linux and the I2C APIs. It needs the
-         OmniVision Camera Chip support as well. You must say Y or M to
-         "Video For Linux", "I2C Support" and "OmniVision Camera Chip
-         support" to use this driver.
+         See <file:Documentation/video4linux/w9968cf.txt> for more info.
 
          To compile this driver as a module, choose M here: the
          module will be called w9968cf.
 
-config USB_ZC0301
-       tristate "USB ZC0301 Image Processor and Control Chip support"
-       depends on USB && VIDEO_DEV
-       ---help---
-         Say Y here if you want support for cameras based on the ZC0301
-         Image Processor and Control Chip.
-
-         See <file:Documentation/usb/zc0301.txt> for more informations.
-
-         This driver uses the Video For Linux API. You must say Y or M to
-         "Video For Linux" to use this driver.
-
-         To compile this driver as a module, choose M here: the
-         module will be called zc0301.
-
-config USB_PWC
-       tristate "USB Philips Cameras"
-       depends on USB && VIDEO_DEV
-       ---help---
-         Say Y or M here if you want to use one of these Philips & OEM
-         webcams:
-          * Philips PCA645, PCA646
-          * Philips PCVC675, PCVC680, PCVC690
-          * Philips PCVC720/40, PCVC730, PCVC740, PCVC750
-          * Askey VC010
-          * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro'
-            and 'Orbit'/'Sphere'
-          * Samsung MPC-C10, MPC-C30
-          * Creative Webcam 5, Pro Ex
-          * SOTEC Afina Eye
-          * Visionite VCS-UC300, VCS-UM100
-
-         The PCA635, PCVC665 and PCVC720/20 are not supported by this driver
-         and never will be, but the 665 and 720/20 are supported by other
-         drivers.
-
-         See <file:Documentation/usb/philips.txt> for more information and
-         installation instructions.
-
-         The built-in microphone is enabled by selecting USB Audio support.
-
-         This driver uses the Video For Linux API. You must say Y or M to
-         "Video For Linux" (under Character Devices) to use this driver.
-         Information on this API and pointers to "v4l" programs may be found
-         at <file:Documentation/video4linux/API.html>.
+source "drivers/media/video/zc0301/Kconfig"
 
-         To compile this driver as a module, choose M here: the
-         module will be called pwc.
+source "drivers/media/video/pwc/Kconfig"
 
 endmenu # V4L USB devices
 
index 1c0e72e..4092a5e 100644 (file)
@@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_EM28XX) += tvp5150.o
 obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
 obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
 obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
+obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
 obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/
 obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
 obj-$(CONFIG_VIDEO_MXB) += saa7111.o tda9840.o tea6415c.o tea6420.o mxb.o
@@ -64,9 +65,8 @@ obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
 obj-$(CONFIG_VIDEO_CX25840) += cx25840/
 obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
 obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
-
-et61x251-objs   := et61x251_core.o et61x251_tas5130d1b.o
-zc0301-objs     := zc0301_core.o zc0301_pas202bcb.o
+obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
+obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 
 obj-$(CONFIG_USB_DABUSB)        += dabusb.o
 obj-$(CONFIG_USB_DSBR)          += dsbr100.o
index 74def9c..423e954 100644 (file)
@@ -973,12 +973,12 @@ audio_mux(struct bttv *btv, int input, int mute)
                   For now this is sufficient. */
                switch (input) {
                case TVAUDIO_INPUT_RADIO:
-                       route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1,
-                                   MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART);
+                       route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+                                   MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
                        break;
                case TVAUDIO_INPUT_EXTERN:
-                       route.input = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1,
-                                   MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART);
+                       route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+                                   MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
                        break;
                case TVAUDIO_INPUT_INTERN:
                        /* Yes, this is the same input as for RADIO. I doubt
@@ -986,8 +986,8 @@ audio_mux(struct bttv *btv, int input, int mute)
                           input is the BTTV_BOARD_AVERMEDIA98. I wonder how
                           that was tested. My guess is that the whole INTERN
                           input does not work. */
-                       route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1,
-                                   MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART);
+                       route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+                                   MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
                        break;
                case TVAUDIO_INPUT_TUNER:
                default:
@@ -1023,14 +1023,11 @@ audio_input(struct bttv *btv, int input)
 static void
 i2c_vidiocschan(struct bttv *btv)
 {
-       struct video_channel c;
+       v4l2_std_id std = bttv_tvnorms[btv->tvnorm].v4l2_id;
 
-       memset(&c,0,sizeof(c));
-       c.norm    = btv->tvnorm;
-       c.channel = btv->input;
-       bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c);
+       bttv_call_i2c_clients(btv, VIDIOC_S_STD, &std);
        if (btv->c.type == BTTV_BOARD_VOODOOTV_FM)
-               bttv_tda9880_setnorm(btv,c.norm);
+               bttv_tda9880_setnorm(btv,btv->tvnorm);
 }
 
 static int
@@ -1184,11 +1181,27 @@ static int get_control(struct bttv *btv, struct v4l2_control *c)
                        break;
        if (i == BTTV_CTLS)
                return -EINVAL;
-       if (i >= 4 && i <= 8) {
+       if (btv->audio_hook && i >= 4 && i <= 8) {
                memset(&va,0,sizeof(va));
-               bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
-               if (btv->audio_hook)
-                       btv->audio_hook(btv,&va,0);
+               btv->audio_hook(btv,&va,0);
+               switch (c->id) {
+               case V4L2_CID_AUDIO_MUTE:
+                       c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0;
+                       break;
+               case V4L2_CID_AUDIO_VOLUME:
+                       c->value = va.volume;
+                       break;
+               case V4L2_CID_AUDIO_BALANCE:
+                       c->value = va.balance;
+                       break;
+               case V4L2_CID_AUDIO_BASS:
+                       c->value = va.bass;
+                       break;
+               case V4L2_CID_AUDIO_TREBLE:
+                       c->value = va.treble;
+                       break;
+               }
+               return 0;
        }
        switch (c->id) {
        case V4L2_CID_BRIGHTNESS:
@@ -1205,19 +1218,11 @@ static int get_control(struct bttv *btv, struct v4l2_control *c)
                break;
 
        case V4L2_CID_AUDIO_MUTE:
-               c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0;
-               break;
        case V4L2_CID_AUDIO_VOLUME:
-               c->value = va.volume;
-               break;
        case V4L2_CID_AUDIO_BALANCE:
-               c->value = va.balance;
-               break;
        case V4L2_CID_AUDIO_BASS:
-               c->value = va.bass;
-               break;
        case V4L2_CID_AUDIO_TREBLE:
-               c->value = va.treble;
+               bttv_call_i2c_clients(btv,VIDIOC_G_CTRL,c);
                break;
 
        case V4L2_CID_PRIVATE_CHROMA_AGC:
@@ -1269,11 +1274,35 @@ static int set_control(struct bttv *btv, struct v4l2_control *c)
                        break;
        if (i == BTTV_CTLS)
                return -EINVAL;
-       if (i >= 4 && i <= 8) {
+       if (btv->audio_hook && i >= 4 && i <= 8) {
                memset(&va,0,sizeof(va));
-               bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
-               if (btv->audio_hook)
-                       btv->audio_hook(btv,&va,0);
+               btv->audio_hook(btv,&va,0);
+               switch (c->id) {
+               case V4L2_CID_AUDIO_MUTE:
+                       if (c->value) {
+                               va.flags |= VIDEO_AUDIO_MUTE;
+                               audio_mute(btv, 1);
+                       } else {
+                               va.flags &= ~VIDEO_AUDIO_MUTE;
+                               audio_mute(btv, 0);
+                       }
+                       break;
+
+               case V4L2_CID_AUDIO_VOLUME:
+                       va.volume = c->value;
+                       break;
+               case V4L2_CID_AUDIO_BALANCE:
+                       va.balance = c->value;
+                       break;
+               case V4L2_CID_AUDIO_BASS:
+                       va.bass = c->value;
+                       break;
+               case V4L2_CID_AUDIO_TREBLE:
+                       va.treble = c->value;
+                       break;
+               }
+               btv->audio_hook(btv,&va,1);
+               return 0;
        }
        switch (c->id) {
        case V4L2_CID_BRIGHTNESS:
@@ -1289,26 +1318,13 @@ static int set_control(struct bttv *btv, struct v4l2_control *c)
                bt848_sat(btv,c->value);
                break;
        case V4L2_CID_AUDIO_MUTE:
-               if (c->value) {
-                       va.flags |= VIDEO_AUDIO_MUTE;
-                       audio_mute(btv, 1);
-               } else {
-                       va.flags &= ~VIDEO_AUDIO_MUTE;
-                       audio_mute(btv, 0);
-               }
-               break;
-
+               audio_mute(btv, c->value);
+               /* fall through */
        case V4L2_CID_AUDIO_VOLUME:
-               va.volume = c->value;
-               break;
        case V4L2_CID_AUDIO_BALANCE:
-               va.balance = c->value;
-               break;
        case V4L2_CID_AUDIO_BASS:
-               va.bass = c->value;
-               break;
        case V4L2_CID_AUDIO_TREBLE:
-               va.treble = c->value;
+               bttv_call_i2c_clients(btv,VIDIOC_S_CTRL,c);
                break;
 
        case V4L2_CID_PRIVATE_CHROMA_AGC:
@@ -1364,11 +1380,6 @@ static int set_control(struct bttv *btv, struct v4l2_control *c)
        default:
                return -EINVAL;
        }
-       if (i >= 4 && i <= 8) {
-               bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
-               if (btv->audio_hook)
-                       btv->audio_hook(btv,&va,1);
-       }
        return 0;
 }
 
@@ -1591,12 +1602,16 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
        }
        case VIDIOCSFREQ:
        {
-               unsigned long *freq = arg;
+               struct v4l2_frequency freq;
+
+               memset(&freq, 0, sizeof(freq));
+               freq.frequency = *(unsigned long *)arg;
                mutex_lock(&btv->lock);
-               btv->freq=*freq;
-               bttv_call_i2c_clients(btv,VIDIOCSFREQ,freq);
+               freq.type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+               btv->freq = *(unsigned long *)arg;
+               bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,&freq);
                if (btv->has_matchbox && btv->radio_user)
-                       tea5757_set_freq(btv,*freq);
+                       tea5757_set_freq(btv,*(unsigned long *)arg);
                mutex_unlock(&btv->lock);
                return 0;
        }
@@ -1827,33 +1842,26 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
                        return -EINVAL;
                mutex_lock(&btv->lock);
                memset(t,0,sizeof(*t));
+               t->rxsubchans = V4L2_TUNER_SUB_MONO;
+               bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
                strcpy(t->name, "Television");
-               t->type       = V4L2_TUNER_ANALOG_TV;
                t->capability = V4L2_TUNER_CAP_NORM;
-               t->rxsubchans = V4L2_TUNER_SUB_MONO;
+               t->type       = V4L2_TUNER_ANALOG_TV;
                if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
                        t->signal = 0xffff;
-               {
-                       struct video_tuner tuner;
-
-                       memset(&tuner, 0, sizeof (tuner));
-                       tuner.rangehigh = 0xffffffffUL;
-                       bttv_call_i2c_clients(btv, VIDIOCGTUNER, &tuner);
-                       t->rangelow = tuner.rangelow;
-                       t->rangehigh = tuner.rangehigh;
-               }
-               {
+
+               if (btv->audio_hook) {
                        /* Hmmm ... */
                        struct video_audio va;
                        memset(&va, 0, sizeof(struct video_audio));
-                       bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
-                       if (btv->audio_hook)
-                               btv->audio_hook(btv,&va,0);
+                       btv->audio_hook(btv,&va,0);
+                       t->audmode    = V4L2_TUNER_MODE_MONO;
+                       t->rxsubchans = V4L2_TUNER_SUB_MONO;
                        if(va.mode & VIDEO_SOUND_STEREO) {
-                               t->audmode     = V4L2_TUNER_MODE_STEREO;
-                               t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+                               t->audmode    = V4L2_TUNER_MODE_STEREO;
+                               t->rxsubchans = V4L2_TUNER_SUB_STEREO;
                        }
-                       if(va.mode & VIDEO_SOUND_LANG1) {
+                       if(va.mode & VIDEO_SOUND_LANG2) {
                                t->audmode    = V4L2_TUNER_MODE_LANG1;
                                t->rxsubchans = V4L2_TUNER_SUB_LANG1
                                        | V4L2_TUNER_SUB_LANG2;
@@ -1872,10 +1880,10 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
                if (0 != t->index)
                        return -EINVAL;
                mutex_lock(&btv->lock);
-               {
+               bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t);
+               if (btv->audio_hook) {
                        struct video_audio va;
                        memset(&va, 0, sizeof(struct video_audio));
-                       bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
                        if (t->audmode == V4L2_TUNER_MODE_MONO)
                                va.mode = VIDEO_SOUND_MONO;
                        else if (t->audmode == V4L2_TUNER_MODE_STEREO ||
@@ -1885,9 +1893,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
                                va.mode = VIDEO_SOUND_LANG1;
                        else if (t->audmode == V4L2_TUNER_MODE_LANG2)
                                va.mode = VIDEO_SOUND_LANG2;
-                       bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
-                       if (btv->audio_hook)
-                               btv->audio_hook(btv,&va,1);
+                       btv->audio_hook(btv,&va,1);
                }
                mutex_unlock(&btv->lock);
                return 0;
@@ -1912,7 +1918,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
                        return -EINVAL;
                mutex_lock(&btv->lock);
                btv->freq = f->frequency;
-               bttv_call_i2c_clients(btv,VIDIOCSFREQ,&btv->freq);
+               bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,f);
                if (btv->has_matchbox && btv->radio_user)
                        tea5757_set_freq(btv,btv->freq);
                mutex_unlock(&btv->lock);
@@ -1920,7 +1926,9 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
        }
        case VIDIOC_LOG_STATUS:
        {
+               printk(KERN_INFO "bttv%d: =================  START STATUS CARD #%d  =================\n", btv->c.nr, btv->c.nr);
                bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL);
+               printk(KERN_INFO "bttv%d: ==================  END STATUS CARD #%d  ==================\n", btv->c.nr, btv->c.nr);
                return 0;
        }
 
@@ -2870,12 +2878,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
                        return 0;
                }
                *c = bttv_ctls[i];
-               if (i >= 4 && i <= 8) {
+               if (btv->audio_hook && i >= 4 && i <= 8) {
                        struct video_audio va;
                        memset(&va,0,sizeof(va));
-                       bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
-                       if (btv->audio_hook)
-                               btv->audio_hook(btv,&va,0);
+                       btv->audio_hook(btv,&va,0);
                        switch (bttv_ctls[i].id) {
                        case V4L2_CID_AUDIO_VOLUME:
                                if (!(va.flags & VIDEO_AUDIO_VOLUME))
index e20ff23..8c9f0f7 100644 (file)
@@ -184,7 +184,7 @@ void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f)
                - tvnorm->vbistart[0];
        count1 = (s64) f->fmt.vbi.start[1] + f->fmt.vbi.count[1]
                - tvnorm->vbistart[1];
-       count  = clamp (max (count0, count1), 1LL, (s64) VBI_MAXLINES);
+       count  = clamp (max (count0, count1), (s64) 1, (s64) VBI_MAXLINES);
 
        f->fmt.vbi.start[0] = tvnorm->vbistart[0];
        f->fmt.vbi.start[1] = tvnorm->vbistart[1];
index 2227c56..85d84e8 100644 (file)
@@ -64,14 +64,13 @@ MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("video");
 #endif
 
-static unsigned short colorspace_conv = 0;
+static unsigned short colorspace_conv;
 module_param(colorspace_conv, ushort, 0444);
 MODULE_PARM_DESC(colorspace_conv,
-                "\n<n> Colorspace conversion:"
-                "\n0 = disable"
-                "\n1 = enable"
-                "\nDefault value is 0"
-                "\n");
+                 " Colorspace conversion:"
+                 "\n  0 = disable, 1 = enable"
+                 "\n  Default value is 0"
+                 );
 
 #define ABOUT "V4L-Driver for Vision CPiA based cameras"
 
@@ -4042,7 +4041,7 @@ static int __init cpia_init(void)
               "allowed, it is disabled by default now. Users should fix the "
               "applications in case they don't work without conversion "
               "reenabled by setting the 'colorspace_conv' module "
-              "parameter to 1");
+              "parameter to 1\n");
 
 #ifdef CONFIG_PROC_FS
        proc_cpia_create();
index 8394283..1764991 100644 (file)
@@ -456,7 +456,7 @@ int cpia2_init_camera(struct camera_data *cam);
 int cpia2_allocate_buffers(struct camera_data *cam);
 void cpia2_free_buffers(struct camera_data *cam);
 long cpia2_read(struct camera_data *cam,
-               char *buf, unsigned long count, int noblock);
+               char __user *buf, unsigned long count, int noblock);
 unsigned int cpia2_poll(struct camera_data *cam,
                        struct file *filp, poll_table *wait);
 int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma);
index a4540e8..9a4b813 100644 (file)
@@ -19,8 +19,9 @@
 #include <linux/videodev2.h>
 #include <linux/i2c.h>
 #include <media/v4l2-common.h>
+#include <media/cx25840.h>
 
-#include "cx25840.h"
+#include "cx25840-core.h"
 
 static int set_audclk_freq(struct i2c_client *client, u32 freq)
 {
index a65b3cc..a961bb2 100644 (file)
@@ -32,8 +32,9 @@
 #include <linux/videodev2.h>
 #include <linux/i2c.h>
 #include <media/v4l2-common.h>
+#include <media/cx25840.h>
 
-#include "cx25840.h"
+#include "cx25840-core.h"
 
 MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver");
 MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford");
@@ -668,6 +669,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
 {
        struct cx25840_state *state = i2c_get_clientdata(client);
        struct v4l2_tuner *vt = arg;
+       struct v4l2_routing *route = arg;
 
        switch (cmd) {
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -749,19 +751,21 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
                state->radio = 1;
                break;
 
-       case VIDIOC_G_INPUT:
-               *(int *)arg = state->vid_input;
+       case VIDIOC_INT_G_VIDEO_ROUTING:
+               route->input = state->vid_input;
+               route->output = 0;
                break;
 
-       case VIDIOC_S_INPUT:
-               return set_input(client, *(enum cx25840_video_input *)arg, state->aud_input);
+       case VIDIOC_INT_S_VIDEO_ROUTING:
+               return set_input(client, route->input, state->aud_input);
 
-       case VIDIOC_S_AUDIO:
-       {
-               struct v4l2_audio *input = arg;
+       case VIDIOC_INT_G_AUDIO_ROUTING:
+               route->input = state->aud_input;
+               route->output = 0;
+               break;
 
-               return set_input(client, state->vid_input, input->index);
-       }
+       case VIDIOC_INT_S_AUDIO_ROUTING:
+               return set_input(client, state->vid_input, route->input);
 
        case VIDIOC_S_FREQUENCY:
                input_change(client);
similarity index 72%
rename from drivers/media/video/cx25840/cx25840.h
rename to drivers/media/video/cx25840/cx25840-core.h
index dd70664..1736929 100644 (file)
@@ -1,4 +1,4 @@
-/* cx25840 API header
+/* cx25840 internal API header
  *
  * Copyright (C) 2003-2004 Chris Kennedy
  *
@@ -17,8 +17,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-#ifndef _CX25840_H_
-#define _CX25840_H_
+#ifndef _CX25840_CORE_H_
+#define _CX25840_CORE_H_
 
 
 #include <linux/videodev2.h>
    providing this information. */
 #define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0)
 
-enum cx25840_video_input {
-       /* Composite video inputs In1-In8 */
-       CX25840_COMPOSITE1 = 1,
-       CX25840_COMPOSITE2,
-       CX25840_COMPOSITE3,
-       CX25840_COMPOSITE4,
-       CX25840_COMPOSITE5,
-       CX25840_COMPOSITE6,
-       CX25840_COMPOSITE7,
-       CX25840_COMPOSITE8,
-
-       /* S-Video inputs consist of one luma input (In1-In4) ORed with one
-          chroma input (In5-In8) */
-       CX25840_SVIDEO_LUMA1 = 0x10,
-       CX25840_SVIDEO_LUMA2 = 0x20,
-       CX25840_SVIDEO_LUMA3 = 0x30,
-       CX25840_SVIDEO_LUMA4 = 0x40,
-       CX25840_SVIDEO_CHROMA4 = 0x400,
-       CX25840_SVIDEO_CHROMA5 = 0x500,
-       CX25840_SVIDEO_CHROMA6 = 0x600,
-       CX25840_SVIDEO_CHROMA7 = 0x700,
-       CX25840_SVIDEO_CHROMA8 = 0x800,
-
-       /* S-Video aliases for common luma/chroma combinations */
-       CX25840_SVIDEO1 = 0x510,
-       CX25840_SVIDEO2 = 0x620,
-       CX25840_SVIDEO3 = 0x730,
-       CX25840_SVIDEO4 = 0x840,
-};
-
-enum cx25840_audio_input {
-       /* Audio inputs: serial or In4-In8 */
-       CX25840_AUDIO_SERIAL,
-       CX25840_AUDIO4 = 4,
-       CX25840_AUDIO5,
-       CX25840_AUDIO6,
-       CX25840_AUDIO7,
-       CX25840_AUDIO8,
-};
-
 struct cx25840_state {
        int pvr150_workaround;
        int radio;
index e1a7823..f59ced1 100644 (file)
 #include <linux/i2c-algo-bit.h>
 #include <linux/firmware.h>
 #include <media/v4l2-common.h>
+#include <media/cx25840.h>
 
-#include "cx25840.h"
+#include "cx25840-core.h"
 
 #define FWFILE "v4l-cx25840.fw"
-#define FWSEND 1024
+
+/*
+ * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
+ * size of the firmware chunks sent down the I2C bus to the chip.
+ * Previously this had been set to 1024 but unfortunately some I2C
+ * implementations can't transfer data in such big gulps.
+ * Specifically, the pvrusb2 driver has a hard limit of around 60
+ * bytes, due to the encapsulation there of I2C traffic into USB
+ * messages.  So we have to significantly reduce this parameter.
+ */
+#define FWSEND 48
 
 #define FWDEV(x) &((x)->adapter->dev)
 
index e96fd1f..57feca2 100644 (file)
@@ -19,8 +19,9 @@
 #include <linux/videodev2.h>
 #include <linux/i2c.h>
 #include <media/v4l2-common.h>
+#include <media/cx25840.h>
 
-#include "cx25840.h"
+#include "cx25840-core.h"
 
 static int odd_parity(u8 c)
 {
@@ -151,7 +152,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
        case VIDIOC_G_FMT:
        {
                static u16 lcr2vbi[] = {
-                       0, V4L2_SLICED_TELETEXT_PAL_B, 0,       /* 1 */
+                       0, V4L2_SLICED_TELETEXT_B, 0,   /* 1 */
                        0, V4L2_SLICED_WSS_625, 0,      /* 4 */
                        V4L2_SLICED_CAPTION_525,        /* 6 */
                        0, 0, V4L2_SLICED_VPS, 0, 0,    /* 9 */
@@ -231,7 +232,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
                for (i = 7; i <= 23; i++) {
                        for (x = 0; x <= 1; x++) {
                                switch (svbi->service_lines[1-x][i]) {
-                               case V4L2_SLICED_TELETEXT_PAL_B:
+                               case V4L2_SLICED_TELETEXT_B:
                                        lcr[i] |= 1 << (4 * x);
                                        break;
                                case V4L2_SLICED_WSS_625:
@@ -282,7 +283,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
 
                switch (id2) {
                case 1:
-                       id2 = V4L2_SLICED_TELETEXT_PAL_B;
+                       id2 = V4L2_SLICED_TELETEXT_B;
                        break;
                case 4:
                        id2 = V4L2_SLICED_WSS_625;
index ff0f723..6302739 100644 (file)
@@ -1,3 +1,7 @@
+config VIDEO_CX88_VP3054
+       tristate
+       depends on VIDEO_CX88_DVB && DVB_MT352
+
 config VIDEO_CX88
        tristate "Conexant 2388x (bt878 successor) support"
        depends on VIDEO_DEV && PCI && I2C
@@ -25,7 +29,7 @@ config VIDEO_CX88_ALSA
 
          It only works with boards with function 01 enabled.
          To check if your board supports, use lspci -n.
-         If supported, you should see 1471:8801 or 1471:8811
+         If supported, you should see 14f1:8801 or 14f1:8811
          PCI device.
 
          To compile this driver as a module, choose M here: the
@@ -73,10 +77,11 @@ config VIDEO_CX88_DVB_MT352
          This adds DVB-T support for cards based on the
          Connexant 2388x chip and the MT352 demodulator.
 
-config VIDEO_CX88_VP3054
-       tristate "VP-3054 Secondary I2C Bus Support"
-       default m
-       depends on DVB_MT352
+config VIDEO_CX88_DVB_VP3054
+       bool "VP-3054 Secondary I2C Bus Support"
+       default y
+       depends on VIDEO_CX88_DVB_MT352
+       select VIDEO_CX88_VP3054
        ---help---
          This adds DVB-T support for cards based on the
          Connexant 2388x chip and the MT352 demodulator,
index f62fd70..3ba3439 100644 (file)
@@ -151,8 +151,8 @@ struct em28xx_board em28xx_boards[] = {
                },{
                        .type     = EM28XX_VMUX_SVIDEO,
                        .vmux     = 2,
-                       .amux     = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1,
-                                       MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART),
+                       .amux     = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+                                       MSP_DSP_IN_SCART, MSP_DSP_IN_SCART),
                }},
        },
        [EM2820_BOARD_MSI_VOX_USB_2] = {
index dfba33d..ddc92cb 100644 (file)
@@ -222,7 +222,7 @@ static void video_mux(struct em28xx *dev, int index)
                if (dev->i2s_speed)
                        em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed);
                route.input = dev->ctl_ainput;
-               route.output = MSP_OUTPUT(MSP_OUT_SCART1_DA);
+               route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
                /* Note: this is msp3400 specific */
                em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route);
                ainput = EM28XX_AUDIO_SRC_TUNER;
@@ -1141,26 +1141,16 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
        case VIDIOC_G_TUNER:
        {
                struct v4l2_tuner *t = arg;
-               int status = 0;
 
                if (0 != t->index)
                        return -EINVAL;
 
                memset(t, 0, sizeof(*t));
                strcpy(t->name, "Tuner");
-               t->type = V4L2_TUNER_ANALOG_TV;
-               t->capability = V4L2_TUNER_CAP_NORM;
-               t->rangehigh = 0xffffffffUL;    /* FIXME: set correct range */
-/*             t->signal = 0xffff;*/
-/*             em28xx_i2c_call_clients(dev,VIDIOC_G_TUNER,t);*/
-               /* No way to get signal strength? */
                mutex_lock(&dev->lock);
-               em28xx_i2c_call_clients(dev, DECODER_GET_STATUS,
-                                       &status);
+               /* let clients fill in the remainder of this struct */
+               em28xx_i2c_call_clients(dev, cmd, t);
                mutex_unlock(&dev->lock);
-               t->signal =
-                       (status & DECODER_STATUS_GOOD) != 0 ? 0xffff : 0;
-
                em28xx_videodbg("VIDIO_G_TUNER: signal=%x, afc=%x\n", t->signal,
                                t->afc);
                return 0;
@@ -1168,26 +1158,13 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
        case VIDIOC_S_TUNER:
        {
                struct v4l2_tuner *t = arg;
-               int status = 0;
 
                if (0 != t->index)
                        return -EINVAL;
-               memset(t, 0, sizeof(*t));
-               strcpy(t->name, "Tuner");
-               t->type = V4L2_TUNER_ANALOG_TV;
-               t->capability = V4L2_TUNER_CAP_NORM;
-               t->rangehigh = 0xffffffffUL;    /* FIXME: set correct range */
-/*             t->signal = 0xffff; */
-               /* No way to get signal strength? */
                mutex_lock(&dev->lock);
-               em28xx_i2c_call_clients(dev, DECODER_GET_STATUS,
-                                       &status);
+               /* let clients handle this */
+               em28xx_i2c_call_clients(dev, cmd, t);
                mutex_unlock(&dev->lock);
-               t->signal =
-                       (status & DECODER_STATUS_GOOD) != 0 ? 0xffff : 0;
-
-               em28xx_videodbg("VIDIO_S_TUNER: signal=%x, afc=%x\n",
-                               t->signal, t->afc);
                return 0;
        }
        case VIDIOC_G_FREQUENCY:
diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig
new file mode 100644 (file)
index 0000000..6c43a90
--- /dev/null
@@ -0,0 +1,14 @@
+config USB_ET61X251
+       tristate "USB ET61X[12]51 PC Camera Controller support"
+       depends on USB && VIDEO_DEV
+       ---help---
+         Say Y here if you want support for cameras based on Etoms ET61X151
+         or ET61X251 PC Camera Controllers.
+
+         See <file:Documentation/video4linux/et61x251.txt> for more info.
+
+         This driver uses the Video For Linux API. You must say Y or M to
+         "Video For Linux" to use this driver.
+
+         To compile this driver as a module, choose M here: the
+         module will be called et61x251.
index 95bacf4..7e66d83 100644 (file)
@@ -411,6 +411,9 @@ static int ir_probe(struct i2c_adapter *adap)
        case I2C_HW_B_BT848:
                probe = probe_bttv;
                break;
+       case I2C_HW_B_CX2341X:
+               probe = probe_bttv;
+               break;
        case I2C_HW_SAA7134:
                probe = probe_saa7134;
                break;
index c40e8ba..b806999 100644 (file)
@@ -279,20 +279,8 @@ void msp_set_scart(struct i2c_client *client, int in, int out)
        msp_write_dsp(client, 0x13, state->acb);
 
        /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */
-       msp_write_dem(client, 0x40, state->i2s_mode);
-}
-
-void msp_set_mute(struct i2c_client *client)
-{
-       struct msp_state *state = i2c_get_clientdata(client);
-
-       v4l_dbg(1, msp_debug, client, "mute audio\n");
-       msp_write_dsp(client, 0x0000, 0);
-       msp_write_dsp(client, 0x0007, 1);
-       if (state->has_scart2_out_volume)
-               msp_write_dsp(client, 0x0040, 1);
-       if (state->has_headphones)
-               msp_write_dsp(client, 0x0006, 0);
+       if (state->has_i2s_conf)
+               msp_write_dem(client, 0x40, state->i2s_mode);
 }
 
 void msp_set_audio(struct i2c_client *client)
@@ -300,17 +288,19 @@ void msp_set_audio(struct i2c_client *client)
        struct msp_state *state = i2c_get_clientdata(client);
        int bal = 0, bass, treble, loudness;
        int val = 0;
+       int reallymuted = state->muted | state->scan_in_progress;
 
-       if (!state->muted)
+       if (!reallymuted)
                val = (state->volume * 0x7f / 65535) << 8;
 
-       v4l_dbg(1, msp_debug, client, "mute=%s volume=%d\n",
-               state->muted ? "on" : "off", state->volume);
+       v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
+               state->muted ? "on" : "off", state->scan_in_progress ? "yes" : "no",
+               state->volume);
 
        msp_write_dsp(client, 0x0000, val);
-       msp_write_dsp(client, 0x0007, state->muted ? 0x1 : (val | 0x1));
+       msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
        if (state->has_scart2_out_volume)
-               msp_write_dsp(client, 0x0040, state->muted ? 0x1 : (val | 0x1));
+               msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
        if (state->has_headphones)
                msp_write_dsp(client, 0x0006, val);
        if (!state->has_sound_processing)
@@ -346,7 +336,6 @@ static void msp_wake_thread(struct i2c_client *client)
 
        if (NULL == state->kthread)
                return;
-       msp_set_mute(client);
        state->watch_stereo = 0;
        state->restart = 1;
        wake_up_interruptible(&state->wq);
@@ -374,19 +363,15 @@ int msp_sleep(struct msp_state *state, int timeout)
 
 /* ------------------------------------------------------------------------ */
 
-static int msp_mode_v4l2_to_v4l1(int rxsubchans)
+static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode)
 {
-       int mode = 0;
-
-       if (rxsubchans & V4L2_TUNER_SUB_STEREO)
-               mode |= VIDEO_SOUND_STEREO;
-       if (rxsubchans & V4L2_TUNER_SUB_LANG2)
-               mode |= VIDEO_SOUND_LANG2 | VIDEO_SOUND_STEREO;
-       if (rxsubchans & V4L2_TUNER_SUB_LANG1)
-               mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_STEREO;
-       if (mode == 0)
-               mode |= VIDEO_SOUND_MONO;
-       return mode;
+       if (rxsubchans == V4L2_TUNER_SUB_MONO)
+               return VIDEO_SOUND_MONO;
+       if (rxsubchans == V4L2_TUNER_SUB_STEREO)
+               return VIDEO_SOUND_STEREO;
+       if (audmode == V4L2_TUNER_MODE_LANG2)
+               return VIDEO_SOUND_LANG2;
+       return VIDEO_SOUND_LANG1;
 }
 
 static int msp_mode_v4l1_to_v4l2(int mode)
@@ -605,7 +590,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
                        break;
                if (state->opmode == OPMODE_AUTOSELECT)
                        msp_detect_stereo(client);
-               va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans);
+               va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans, state->audmode);
                break;
        }
 
@@ -620,7 +605,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
                state->treble = va->treble;
                msp_set_audio(client);
 
-               if (va->mode != 0 && state->radio == 0) {
+               if (va->mode != 0 && state->radio == 0 &&
+                   state->audmode != msp_mode_v4l1_to_v4l2(va->mode)) {
                        state->audmode = msp_mode_v4l1_to_v4l2(va->mode);
                        msp_set_audmode(client);
                }
@@ -687,21 +673,23 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
                int sc_in = rt->input & 0x7;
                int sc1_out = rt->output & 0xf;
                int sc2_out = (rt->output >> 4) & 0xf;
-               u16 val;
+               u16 val, reg;
 
+               if (state->routing.input == rt->input &&
+                   state->routing.output == rt->output)
+                       break;
                state->routing = *rt;
-               if (state->opmode == OPMODE_AUTOSELECT) {
-                       val = msp_read_dem(client, 0x30) & ~0x100;
-                       msp_write_dem(client, 0x30, val | (tuner ? 0x100 : 0));
-               } else {
-                       val = msp_read_dem(client, 0xbb) & ~0x100;
-                       msp_write_dem(client, 0xbb, val | (tuner ? 0x100 : 0));
-               }
                msp_set_scart(client, sc_in, 0);
                msp_set_scart(client, sc1_out, 1);
                msp_set_scart(client, sc2_out, 2);
                msp_set_audmode(client);
-               msp_wake_thread(client);
+               reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
+               val = msp_read_dem(client, reg);
+               if (tuner != ((val >> 8) & 1)) {
+                       msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
+                       /* wake thread when a new tuner input is chosen */
+                       msp_wake_thread(client);
+               }
                break;
        }
 
@@ -715,7 +703,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
                        msp_detect_stereo(client);
                vt->audmode    = state->audmode;
                vt->rxsubchans = state->rxsubchans;
-               vt->capability = V4L2_TUNER_CAP_STEREO |
+               vt->capability |= V4L2_TUNER_CAP_STEREO |
                        V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
                break;
        }
@@ -726,6 +714,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
 
                if (state->radio)  /* TODO: add mono/stereo support for radio */
                        break;
+               if (state->audmode == vt->audmode)
+                       break;
                state->audmode = vt->audmode;
                /* only set audmode */
                msp_set_audmode(client);
@@ -887,7 +877,7 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
 
        memset(state, 0, sizeof(*state));
        state->v4l2_std = V4L2_STD_NTSC;
-       state->audmode = V4L2_TUNER_MODE_LANG1;
+       state->audmode = V4L2_TUNER_MODE_STEREO;
        state->volume = 58880;  /* 0db gain */
        state->balance = 32768; /* 0db gain */
        state->bass = 32768;
@@ -931,13 +921,16 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
        state->has_radio = msp_revision >= 'G';
        /* Has headphones output: not for stripped down products */
        state->has_headphones = msp_prod_lo < 5;
+       /* Has scart2 input: not in stripped down products of the '3' family */
+       state->has_scart2 = msp_family >= 4 || msp_prod_lo < 7;
+       /* Has scart3 input: not in stripped down products of the '3' family */
+       state->has_scart3 = msp_family >= 4 || msp_prod_lo < 5;
        /* Has scart4 input: not in pre D revisions, not in stripped D revs */
        state->has_scart4 = msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5);
-       /* Has scart2 and scart3 inputs and scart2 output: not in stripped
-          down products of the '3' family */
-       state->has_scart23_in_scart2_out = msp_family >= 4 || msp_prod_lo < 5;
+       /* Has scart2 output: not in stripped down products of the '3' family */
+       state->has_scart2_out = msp_family >= 4 || msp_prod_lo < 5;
        /* Has scart2 a volume control? Not in pre-D revisions. */
-       state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart23_in_scart2_out;
+       state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart2_out;
        /* Has a configurable i2s out? */
        state->has_i2s_conf = msp_revision >= 'G' && msp_prod_lo < 7;
        /* Has subwoofer output: not in pre-D revs and not in stripped down products */
index 1940748..4e45104 100644 (file)
@@ -54,8 +54,10 @@ struct msp_state {
        u8 has_radio;
        u8 has_headphones;
        u8 has_ntsc_jp_d_k3;
+       u8 has_scart2;
+       u8 has_scart3;
        u8 has_scart4;
-       u8 has_scart23_in_scart2_out;
+       u8 has_scart2_out;
        u8 has_scart2_out_volume;
        u8 has_i2s_conf;
        u8 has_subwoofer;
@@ -83,6 +85,7 @@ struct msp_state {
        int volume, muted;
        int balance, loudness;
        int bass, treble;
+       int scan_in_progress;
 
        /* thread */
        struct task_struct   *kthread;
@@ -98,7 +101,6 @@ int msp_read_dem(struct i2c_client *client, int addr);
 int msp_read_dsp(struct i2c_client *client, int addr);
 int msp_reset(struct i2c_client *client);
 void msp_set_scart(struct i2c_client *client, int in, int out);
-void msp_set_mute(struct i2c_client *client);
 void msp_set_audio(struct i2c_client *client);
 int msp_sleep(struct msp_state *state, int timeout);
 
index c3984ea..633a102 100644 (file)
@@ -170,7 +170,7 @@ static void msp_set_source(struct i2c_client *client, u16 src)
        msp_write_dsp(client, 0x000a, src);
        msp_write_dsp(client, 0x000b, src);
        msp_write_dsp(client, 0x000c, src);
-       if (state->has_scart23_in_scart2_out)
+       if (state->has_scart2_out)
                msp_write_dsp(client, 0x0041, src);
 }
 
@@ -228,6 +228,7 @@ static void msp3400c_set_audmode(struct i2c_client *client)
        char *modestr = (state->audmode >= 0 && state->audmode < 5) ?
                strmode[state->audmode] : "unknown";
        int src = 0;    /* channel source: FM/AM, nicam or SCART */
+       int audmode = state->audmode;
 
        if (state->opmode == OPMODE_AUTOSELECT) {
                /* this method would break everything, let's make sure
@@ -239,11 +240,29 @@ static void msp3400c_set_audmode(struct i2c_client *client)
                return;
        }
 
+       /* Note: for the C and D revs no NTSC stereo + SAP is possible as
+          the hardware does not support SAP. So the rxsubchans combination
+          of STEREO | LANG2 does not occur. */
+
+       /* switch to mono if only mono is available */
+       if (state->rxsubchans == V4L2_TUNER_SUB_MONO)
+               audmode = V4L2_TUNER_MODE_MONO;
+       /* if bilingual */
+       else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) {
+               /* and mono or stereo, then fallback to lang1 */
+               if (audmode == V4L2_TUNER_MODE_MONO ||
+                   audmode == V4L2_TUNER_MODE_STEREO)
+                       audmode = V4L2_TUNER_MODE_LANG1;
+       }
+       /* if stereo, and audmode is not mono, then switch to stereo */
+       else if (audmode != V4L2_TUNER_MODE_MONO)
+               audmode = V4L2_TUNER_MODE_STEREO;
+
        /* switch demodulator */
        switch (state->mode) {
        case MSP_MODE_FM_TERRA:
                v4l_dbg(1, msp_debug, client, "FM set_audmode: %s\n", modestr);
-               switch (state->audmode) {
+               switch (audmode) {
                case V4L2_TUNER_MODE_STEREO:
                        msp_write_dsp(client, 0x000e, 0x3001);
                        break;
@@ -257,7 +276,7 @@ static void msp3400c_set_audmode(struct i2c_client *client)
                break;
        case MSP_MODE_FM_SAT:
                v4l_dbg(1, msp_debug, client, "SAT set_audmode: %s\n", modestr);
-               switch (state->audmode) {
+               switch (audmode) {
                case V4L2_TUNER_MODE_MONO:
                        msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5));
                        break;
@@ -296,7 +315,8 @@ static void msp3400c_set_audmode(struct i2c_client *client)
        }
 
        /* switch audio */
-       switch (state->audmode) {
+       v4l_dbg(1, msp_debug, client, "set audmode %d\n", audmode);
+       switch (audmode) {
        case V4L2_TUNER_MODE_STEREO:
        case V4L2_TUNER_MODE_LANG1_LANG2:
                src |= 0x0020;
@@ -314,10 +334,6 @@ static void msp3400c_set_audmode(struct i2c_client *client)
                        src = 0x0030;
                break;
        case V4L2_TUNER_MODE_LANG1:
-               /* switch to stereo for stereo transmission, otherwise
-                  keep first language */
-               if (state->rxsubchans & V4L2_TUNER_SUB_STEREO)
-                       src |= 0x0020;
                break;
        case V4L2_TUNER_MODE_LANG2:
                src |= 0x0010;
@@ -367,7 +383,7 @@ static int msp3400c_detect_stereo(struct i2c_client *client)
                if (val > 32767)
                        val -= 65536;
                v4l_dbg(2, msp_debug, client, "stereo detect register: %d\n", val);
-               if (val > 4096) {
+               if (val > 8192) {
                        rxsubchans = V4L2_TUNER_SUB_STEREO;
                } else if (val < -4096) {
                        rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
@@ -464,19 +480,22 @@ int msp3400c_thread(void *data)
                if (state->radio || MSP_MODE_EXTERN == state->mode) {
                        /* no carrier scan, just unmute */
                        v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n");
+                       state->scan_in_progress = 0;
                        msp_set_audio(client);
                        continue;
                }
 
-               /* mute */
-               msp_set_mute(client);
+               /* mute audio */
+               state->scan_in_progress = 1;
+               msp_set_audio(client);
+
                msp3400c_set_mode(client, MSP_MODE_AM_DETECT);
                val1 = val2 = 0;
                max1 = max2 = -1;
                state->watch_stereo = 0;
                state->nicam_on = 0;
 
-               /* some time for the tuner to sync */
+               /* wait for tuner to settle down after a channel change */
                if (msp_sleep(state, 200))
                        goto restart;
 
@@ -552,7 +571,6 @@ int msp3400c_thread(void *data)
                                /* B/G NICAM */
                                state->second = msp3400c_carrier_detect_55[max2].cdo;
                                msp3400c_set_mode(client, MSP_MODE_FM_NICAM1);
-                               msp3400c_set_carrier(client, state->second, state->main);
                                state->nicam_on = 1;
                                state->watch_stereo = 1;
                        } else {
@@ -563,7 +581,6 @@ int msp3400c_thread(void *data)
                        /* PAL I NICAM */
                        state->second = MSP_CARRIER(6.552);
                        msp3400c_set_mode(client, MSP_MODE_FM_NICAM2);
-                       msp3400c_set_carrier(client, state->second, state->main);
                        state->nicam_on = 1;
                        state->watch_stereo = 1;
                        break;
@@ -577,13 +594,11 @@ int msp3400c_thread(void *data)
                                /* L NICAM or AM-mono */
                                state->second = msp3400c_carrier_detect_65[max2].cdo;
                                msp3400c_set_mode(client, MSP_MODE_AM_NICAM);
-                               msp3400c_set_carrier(client, state->second, state->main);
                                state->watch_stereo = 1;
                        } else if (max2 == 0 && state->has_nicam) {
                                /* D/K NICAM */
                                state->second = msp3400c_carrier_detect_65[max2].cdo;
                                msp3400c_set_mode(client, MSP_MODE_FM_NICAM1);
-                               msp3400c_set_carrier(client, state->second, state->main);
                                state->nicam_on = 1;
                                state->watch_stereo = 1;
                        } else {
@@ -595,25 +610,25 @@ int msp3400c_thread(void *data)
                no_second:
                        state->second = msp3400c_carrier_detect_main[max1].cdo;
                        msp3400c_set_mode(client, MSP_MODE_FM_TERRA);
-                       msp3400c_set_carrier(client, state->second, state->main);
-                       state->rxsubchans = V4L2_TUNER_SUB_MONO;
                        break;
                }
+               msp3400c_set_carrier(client, state->second, state->main);
 
                /* unmute */
-               msp_set_audio(client);
+               state->scan_in_progress = 0;
                msp3400c_set_audmode(client);
+               msp_set_audio(client);
 
                if (msp_debug)
                        msp3400c_print_mode(client);
 
                /* monitor tv audio mode, the first time don't wait
                   so long to get a quick stereo/bilingual result */
-               if (msp_sleep(state, 1000))
-                       goto restart;
+               count = 3;
                while (state->watch_stereo) {
-                       if (msp_sleep(state, 5000))
+                       if (msp_sleep(state, count ? 1000 : 5000))
                                goto restart;
+                       if (count) count--;
                        watch_stereo(client);
                }
        }
@@ -626,7 +641,7 @@ int msp3410d_thread(void *data)
 {
        struct i2c_client *client = data;
        struct msp_state *state = i2c_get_clientdata(client);
-       int val, i, std;
+       int val, i, std, count;
 
        v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n");
 
@@ -644,16 +659,14 @@ int msp3410d_thread(void *data)
                if (state->mode == MSP_MODE_EXTERN) {
                        /* no carrier scan needed, just unmute */
                        v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n");
+                       state->scan_in_progress = 0;
                        msp_set_audio(client);
                        continue;
                }
 
-               /* put into sane state (and mute) */
-               msp_reset(client);
-
-               /* some time for the tuner to sync */
-               if (msp_sleep(state,200))
-                       goto restart;
+               /* mute audio */
+               state->scan_in_progress = 1;
+               msp_set_audio(client);
 
                /* start autodetect. Note: autodetect is not supported for
                   NTSC-M and radio, hence we force the standard in those cases. */
@@ -664,6 +677,10 @@ int msp3410d_thread(void *data)
                state->watch_stereo = 0;
                state->nicam_on = 0;
 
+               /* wait for tuner to settle down after a channel change */
+               if (msp_sleep(state, 200))
+                       goto restart;
+
                if (msp_debug)
                        v4l_dbg(2, msp_debug, client, "setting standard: %s (0x%04x)\n",
                               msp_standard_std_name(std), std);
@@ -693,6 +710,7 @@ int msp3410d_thread(void *data)
                state->main   = msp_stdlist[i].main;
                state->second = msp_stdlist[i].second;
                state->std = val;
+               state->rxsubchans = V4L2_TUNER_SUB_MONO;
 
                if (msp_amsound && !state->radio && (state->v4l2_std & V4L2_STD_SECAM) &&
                                (val != 0x0009)) {
@@ -714,20 +732,17 @@ int msp3410d_thread(void *data)
                        else
                                state->mode = MSP_MODE_FM_NICAM1;
                        /* just turn on stereo */
-                       state->rxsubchans = V4L2_TUNER_SUB_STEREO;
                        state->nicam_on = 1;
                        state->watch_stereo = 1;
                        break;
                case 0x0009:
                        state->mode = MSP_MODE_AM_NICAM;
-                       state->rxsubchans = V4L2_TUNER_SUB_MONO;
                        state->nicam_on = 1;
                        state->watch_stereo = 1;
                        break;
                case 0x0020: /* BTSC */
                        /* The pre-'G' models only have BTSC-mono */
                        state->mode = MSP_MODE_BTSC;
-                       state->rxsubchans = V4L2_TUNER_SUB_MONO;
                        break;
                case 0x0040: /* FM radio */
                        state->mode = MSP_MODE_FM_RADIO;
@@ -737,15 +752,12 @@ int msp3410d_thread(void *data)
                        msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
                        msp3400c_set_carrier(client, MSP_CARRIER(10.7),
                                            MSP_CARRIER(10.7));
-                       /* scart routing (this doesn't belong here I think) */
-                       msp_set_scart(client,SCART_IN2,0);
                        break;
                case 0x0002:
                case 0x0003:
                case 0x0004:
                case 0x0005:
                        state->mode = MSP_MODE_FM_TERRA;
-                       state->rxsubchans = V4L2_TUNER_SUB_MONO;
                        state->watch_stereo = 1;
                        break;
                }
@@ -759,20 +771,19 @@ int msp3410d_thread(void *data)
                if (state->has_i2s_conf)
                        msp_write_dem(client, 0x40, state->i2s_mode);
 
-               /* unmute, restore misc registers */
-               msp_set_audio(client);
-
-               msp_write_dsp(client, 0x13, state->acb);
+               /* unmute */
                msp3400c_set_audmode(client);
+               state->scan_in_progress = 0;
+               msp_set_audio(client);
 
                /* monitor tv audio mode, the first time don't wait
                   so long to get a quick stereo/bilingual result */
-               if (msp_sleep(state, 1000))
-                       goto restart;
+               count = 3;
                while (state->watch_stereo) {
-                       watch_stereo(client);
-                       if (msp_sleep(state, 5000))
+                       if (msp_sleep(state, count ? 1000 : 5000))
                                goto restart;
+                       if (count) count--;
+                       watch_stereo(client);
                }
        }
        v4l_dbg(1, msp_debug, client, "thread: exit\n");
@@ -829,27 +840,27 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
                source = 0; /* mono only */
                matrix = 0x30;
                break;
-       case V4L2_TUNER_MODE_LANG1:
-               source = 3; /* stereo or A */
-               matrix = 0x00;
-               break;
        case V4L2_TUNER_MODE_LANG2:
                source = 4; /* stereo or B */
                matrix = 0x10;
                break;
-       case V4L2_TUNER_MODE_STEREO:
        case V4L2_TUNER_MODE_LANG1_LANG2:
-       default:
                source = 1; /* stereo or A|B */
                matrix = 0x20;
                break;
+       case V4L2_TUNER_MODE_STEREO:
+       case V4L2_TUNER_MODE_LANG1:
+       default:
+               source = 3; /* stereo or A */
+               matrix = 0x00;
+               break;
        }
 
-       if (in == MSP_DSP_OUT_TUNER)
+       if (in == MSP_DSP_IN_TUNER)
                source = (source << 8) | 0x20;
        /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14
           instead of 11, 12, 13. So we add one for that msp version. */
-       else if (in >= MSP_DSP_OUT_MAIN_AVC && state->has_dolby_pro_logic)
+       else if (in >= MSP_DSP_IN_MAIN_AVC && state->has_dolby_pro_logic)
                source = ((in + 1) << 8) | matrix;
        else
                source = (in << 8) | matrix;
@@ -869,7 +880,7 @@ static void msp34xxg_set_sources(struct i2c_client *client)
        msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf);
        msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf);
        msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf);
-       if (state->has_scart23_in_scart2_out)
+       if (state->has_scart2_out)
                msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf);
        msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf);
 }
@@ -887,10 +898,6 @@ static void msp34xxg_reset(struct i2c_client *client)
 
        msp_reset(client);
 
-       /* make sure that input/output is muted (paranoid mode) */
-       /* ACB, mute DSP input, mute SCART 1 */
-       msp_write_dsp(client, 0x13, 0x0f20);
-
        if (state->has_i2s_conf)
                msp_write_dem(client, 0x40, state->i2s_mode);
 
@@ -1028,7 +1035,7 @@ static void msp34xxg_set_audmode(struct i2c_client *client)
 
        if (state->std == 0x20) {
               if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) &&
-                  (state->audmode == V4L2_TUNER_MODE_STEREO ||
+                  (state->audmode == V4L2_TUNER_MODE_LANG1_LANG2 ||
                    state->audmode == V4L2_TUNER_MODE_LANG2)) {
                        msp_write_dem(client, 0x20, 0x21);
               } else {
diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig
new file mode 100644 (file)
index 0000000..8637655
--- /dev/null
@@ -0,0 +1,28 @@
+config USB_PWC
+       tristate "USB Philips Cameras"
+       depends on USB && VIDEO_DEV
+       ---help---
+         Say Y or M here if you want to use one of these Philips & OEM
+         webcams:
+          * Philips PCA645, PCA646
+          * Philips PCVC675, PCVC680, PCVC690
+          * Philips PCVC720/40, PCVC730, PCVC740, PCVC750
+          * Askey VC010
+          * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro'
+            and 'Orbit'/'Sphere'
+          * Samsung MPC-C10, MPC-C30
+          * Creative Webcam 5, Pro Ex
+          * SOTEC Afina Eye
+          * Visionite VCS-UC300, VCS-UM100
+
+         The PCA635, PCVC665 and PCVC720/20 are not supported by this driver
+         and never will be, but the 665 and 720/20 are supported by other
+         drivers.
+
+         See <file:Documentation/usb/philips.txt> for more information and
+         installation instructions.
+
+         The built-in microphone is enabled by selecting USB Audio support.
+
+         To compile this driver as a module, choose M here: the
+         module will be called pwc.
index b050152..dceebc0 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <media/saa7115.h>
 #include <asm/div64.h>
 
 MODULE_DESCRIPTION("Philips SAA7113/SAA7114/SAA7115 video decoder driver");
@@ -53,7 +54,7 @@ module_param(debug, bool, 0644);
 MODULE_PARM_DESC(debug, "Debug level (0-1)");
 
 static unsigned short normal_i2c[] = {
-               0x4a >>1, 0x48 >>1,     /* SAA7113 */
+               0x4a >> 1, 0x48 >> 1,   /* SAA7113 */
                0x42 >> 1, 0x40 >> 1,   /* SAA7114 and SAA7115 */
                I2C_CLIENT_END };
 
@@ -722,16 +723,16 @@ static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
        100 reserved                    NTSC-Japan (3.58MHz)
        */
        if (state->ident == V4L2_IDENT_SAA7113) {
-               u8 reg =  saa7115_read(client, 0x0e) & 0x8f;
+               u8 reg = saa7115_read(client, 0x0e) & 0x8f;
 
                if (std == V4L2_STD_PAL_M) {
-                       reg|=0x30;
+                       reg |= 0x30;
                } else if (std == V4L2_STD_PAL_N) {
-                       reg|=0x20;
+                       reg |= 0x20;
                } else if (std == V4L2_STD_PAL_60) {
-                       reg|=0x10;
+                       reg |= 0x10;
                } else if (std == V4L2_STD_NTSC_M_JP) {
-                       reg|=0x40;
+                       reg |= 0x40;
                }
                saa7115_write(client, 0x0e, reg);
        }
@@ -811,7 +812,7 @@ static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo
        u8 lcr[24];
        int i, x;
 
-       /* saa7113/71144 doesn't yet support VBI */
+       /* saa7113/7114 doesn't yet support VBI */
        if (state->ident != V4L2_IDENT_SAA7115)
                return;
 
@@ -851,7 +852,7 @@ static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo
                                        case 0:
                                                lcr[i] |= 0xf << (4 * x);
                                                break;
-                                       case V4L2_SLICED_TELETEXT_PAL_B:
+                                       case V4L2_SLICED_TELETEXT_B:
                                                lcr[i] |= 1 << (4 * x);
                                                break;
                                        case V4L2_SLICED_CAPTION_525:
@@ -880,7 +881,7 @@ static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo
 static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
 {
        static u16 lcr2vbi[] = {
-               0, V4L2_SLICED_TELETEXT_PAL_B, 0,       /* 1 */
+               0, V4L2_SLICED_TELETEXT_B, 0,   /* 1 */
                0, V4L2_SLICED_CAPTION_525,     /* 4 */
                V4L2_SLICED_WSS_625, 0,         /* 5 */
                V4L2_SLICED_VPS, 0, 0, 0, 0,    /* 7 */
@@ -1045,7 +1046,7 @@ static void saa7115_decode_vbi_line(struct i2c_client *client,
        /* decode payloads */
        switch (id2) {
        case 1:
-               vbi->type = V4L2_SLICED_TELETEXT_PAL_B;
+               vbi->type = V4L2_SLICED_TELETEXT_B;
                break;
        case 4:
                if (!saa7115_odd_parity(p[0]) || !saa7115_odd_parity(p[1]))
@@ -1180,6 +1181,46 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
                state->radio = 1;
                break;
 
+       case VIDIOC_INT_G_VIDEO_ROUTING:
+       {
+               struct v4l2_routing *route = arg;
+
+               route->input = state->input;
+               route->output = 0;
+               break;
+       }
+
+       case VIDIOC_INT_S_VIDEO_ROUTING:
+       {
+               struct v4l2_routing *route = arg;
+
+               v4l_dbg(1, debug, client, "decoder set input %d\n", route->input);
+               /* saa7113 does not have these inputs */
+               if (state->ident == V4L2_IDENT_SAA7113 &&
+                   (route->input == SAA7115_COMPOSITE4 ||
+                    route->input == SAA7115_COMPOSITE5)) {
+                       return -EINVAL;
+               }
+               if (route->input > SAA7115_SVIDEO3)
+                       return -EINVAL;
+               if (state->input == route->input)
+                       break;
+               v4l_dbg(1, debug, client, "now setting %s input\n",
+                       (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite");
+               state->input = route->input;
+
+               /* select mode */
+               saa7115_write(client, 0x02,
+                             (saa7115_read(client, 0x02) & 0xf0) |
+                              state->input);
+
+               /* bypass chrominance trap for S-Video modes */
+               saa7115_write(client, 0x09,
+                             (saa7115_read(client, 0x09) & 0x7f) |
+                              (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
+               break;
+       }
+
        case VIDIOC_G_INPUT:
                *(int *)arg = state->input;
                break;
@@ -1321,7 +1362,7 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind)
 
        saa7115_write(client, 0, 5);
        chip_id = saa7115_read(client, 0) & 0x0f;
-       if (chip_id <3 && chip_id > 5) {
+       if (chip_id < 3 && chip_id > 5) {
                v4l_dbg(1, debug, client, "saa7115 not found\n");
                kfree(client);
                return 0;
@@ -1360,7 +1401,7 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind)
        v4l_dbg(1, debug, client, "writing init values\n");
 
        /* init to 60hz/48khz */
-       if (state->ident==V4L2_IDENT_SAA7113)
+       if (state->ident == V4L2_IDENT_SAA7113)
                saa7115_writeregs(client, saa7113_init_auto_input);
        else
                saa7115_writeregs(client, saa7115_init_auto_input);
index 992c717..133f9e5 100644 (file)
@@ -54,6 +54,7 @@
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <media/saa7127.h>
 
 static int debug = 0;
 static int test_image = 0;
@@ -222,22 +223,6 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = {
        { 0, 0 }
 };
 
-/* Enumeration for the Supported input types */
-enum saa7127_input_type {
-       SAA7127_INPUT_TYPE_NORMAL,
-       SAA7127_INPUT_TYPE_TEST_IMAGE
-};
-
-/* Enumeration for the Supported Output signal types */
-enum saa7127_output_type {
-       SAA7127_OUTPUT_TYPE_BOTH,
-       SAA7127_OUTPUT_TYPE_COMPOSITE,
-       SAA7127_OUTPUT_TYPE_SVIDEO,
-       SAA7127_OUTPUT_TYPE_RGB,
-       SAA7127_OUTPUT_TYPE_YUV_C,
-       SAA7127_OUTPUT_TYPE_YUV_V
-};
-
 /*
  **********************************************************************
  *
@@ -561,7 +546,7 @@ static int saa7127_command(struct i2c_client *client,
 {
        struct saa7127_state *state = i2c_get_clientdata(client);
        struct v4l2_format *fmt = arg;
-       int *iarg = arg;
+       struct v4l2_routing *route = arg;
 
        switch (cmd) {
        case VIDIOC_S_STD:
@@ -573,15 +558,23 @@ static int saa7127_command(struct i2c_client *client,
                *(v4l2_std_id *)arg = state->std;
                break;
 
-       case VIDIOC_S_INPUT:
-               if (state->input_type == *iarg)
-                       break;
-               return saa7127_set_input_type(client, *iarg);
+       case VIDIOC_INT_G_VIDEO_ROUTING:
+               route->input = state->input_type;
+               route->output = state->output_type;
+               break;
 
-       case VIDIOC_S_OUTPUT:
-               if (state->output_type == *iarg)
-                       break;
-               return saa7127_set_output_type(client, *iarg);
+       case VIDIOC_INT_S_VIDEO_ROUTING:
+       {
+               int rc = 0;
+
+               if (state->input_type != route->input) {
+                       rc = saa7127_set_input_type(client, route->input);
+               }
+               if (rc == 0 && state->output_type != route->output) {
+                       rc = saa7127_set_output_type(client, route->output);
+               }
+               return rc;
+       }
 
        case VIDIOC_STREAMON:
        case VIDIOC_STREAMOFF:
index 86671a4..e1c1805 100644 (file)
@@ -39,6 +39,7 @@ config VIDEO_SAA7134_DVB
        tristate "DVB/ATSC Support for saa7134 based TV cards"
        depends on VIDEO_SAA7134 && DVB_CORE
        select VIDEO_BUF_DVB
+       select FW_LOADER
        ---help---
          This adds support for DVB cards based on the
          Philips saa7134 chip.
index fdd7f48..e666a44 100644 (file)
@@ -208,7 +208,7 @@ struct saa7134_board saa7134_boards[] = {
        [SAA7134_BOARD_FLYTVPLATINUM_FM] = {
                /* LifeView FlyTV Platinum FM (LR214WF) */
                /* "Peter Missel <peter.missel@onlinehome.de> */
-               .name           = "LifeView FlyTV Platinum FM",
+               .name           = "LifeView FlyTV Platinum FM / Gold",
                .audio_clock    = 0x00200000,
                .tuner_type     = TUNER_PHILIPS_TDA8290,
                .radio_type     = UNSET,
@@ -2660,7 +2660,7 @@ struct saa7134_board saa7134_boards[] = {
                .mpeg           = SAA7134_MPEG_DVB,
                .inputs = {{
                        .name   = name_comp1,
-                       .vmux   = 0,
+                       .vmux   = 1,
                        .amux   = LINE1,
                },{
                        .name   = name_svideo,
@@ -2671,7 +2671,7 @@ struct saa7134_board saa7134_boards[] = {
        [SAA7134_BOARD_FLYDVBT_LR301] = {
                /* LifeView FlyDVB-T */
                /* Giampiero Giancipoli <gianci@libero.it> */
-               .name           = "LifeView FlyDVB-T",
+               .name           = "LifeView FlyDVB-T / Genius VideoWonder DVB-T",
                .audio_clock    = 0x00200000,
                .tuner_type     = TUNER_ABSENT,
                .radio_type     = UNSET,
@@ -2808,6 +2808,40 @@ struct saa7134_board saa7134_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
        },
+       [SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS] = {
+               .name           = "LifeView FlyDVB-T Hybrid Cardbus",
+               .audio_clock    = 0x00200000,
+               .tuner_type     = TUNER_PHILIPS_TDA8290,
+               .radio_type     = UNSET,
+               .tuner_addr     = ADDR_UNSET,
+               .radio_addr     = ADDR_UNSET,
+               .mpeg           = SAA7134_MPEG_DVB,
+               .gpiomask       = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */
+               .inputs         = {{
+                       .name = name_tv,
+                       .vmux = 1,
+                       .amux = TV,
+                       .gpio = 0x200000,       /* GPIO21=High for TV input */
+                       .tv   = 1,
+               },{
+                       .name = name_svideo,    /* S-Video signal on S-Video input */
+                       .vmux = 8,
+                       .amux = LINE2,
+               },{
+                       .name = name_comp1,     /* Composite signal on S-Video input */
+                       .vmux = 0,
+                       .amux = LINE2,
+               },{
+                       .name = name_comp2,     /* Composite input */
+                       .vmux = 3,
+                       .amux = LINE2,
+               }},
+               .radio = {
+                       .name = name_radio,
+                       .amux = TV,
+                       .gpio = 0x000000,       /* GPIO21=Low for FM radio antenna */
+               },
+       },
 };
 
 const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -3332,6 +3366,30 @@ struct pci_device_id saa7134_pci_tbl[] = {
                .subvendor    = 0x16be,
                .subdevice    = 0x0005,
                .driver_data  = SAA7134_BOARD_MD7134_BRIDGE_2,
+       },{
+               .vendor = PCI_VENDOR_ID_PHILIPS,
+               .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+               .subvendor = 0x1489,
+               .subdevice = 0x0301,
+               .driver_data = SAA7134_BOARD_FLYDVBT_LR301,
+       },{
+               .vendor = PCI_VENDOR_ID_PHILIPS,
+               .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+               .subvendor = 0x5168, /* Animation Technologies (LifeView) */
+               .subdevice = 0x0304,
+               .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM,
+       },{
+               .vendor       = PCI_VENDOR_ID_PHILIPS,
+               .device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+               .subvendor    = 0x5168,
+               .subdevice    = 0x3306,
+               .driver_data  = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
+       },{
+               .vendor       = PCI_VENDOR_ID_PHILIPS,
+               .device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+               .subvendor    = 0x5168,
+               .subdevice    = 0x3502,  /* whats the difference to 0x3306 ?*/
+               .driver_data  = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
        },{
                /* --- boards without eeprom + subsystem ID --- */
                .vendor       = PCI_VENDOR_ID_PHILIPS,
@@ -3462,6 +3520,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
                saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x06);
                break;
        case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
+       case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
                saa_writeb(SAA7134_GPIO_GPMODE3, 0x08);
                saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x00);
                break;
@@ -3633,6 +3692,7 @@ int saa7134_board_init2(struct saa7134_dev *dev)
                }
                break;
        case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
+       case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
                /* make the tda10046 find its eeprom */
                {
                u8 data[] = { 0x3c, 0x33, 0x62};
index 86cfdb8..222a36c 100644 (file)
@@ -1064,6 +1064,10 @@ static int dvb_init(struct saa7134_dev *dev)
                dev->dvb.frontend = tda10046_attach(&tevion_dvbt220rf_config,
                                                    &dev->i2c_adap);
                break;
+       case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
+               dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config,
+                                                   &dev->i2c_adap);
+               break;
 #endif
 #ifdef HAVE_NXT200X
        case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180:
index 31ba293..353af3a 100644 (file)
@@ -220,6 +220,7 @@ struct saa7134_format {
 #define SAA7134_BOARD_AVERMEDIA_A169_B 91
 #define SAA7134_BOARD_AVERMEDIA_A169_B1 92
 #define SAA7134_BOARD_MD7134_BRIDGE_2     93
+#define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94
 
 #define SAA7134_MAXBOARDS 8
 #define SAA7134_INPUT_MAX 8
diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig
new file mode 100644 (file)
index 0000000..55f2bc1
--- /dev/null
@@ -0,0 +1,11 @@
+config USB_SN9C102
+       tristate "USB SN9C10x PC Camera Controller support"
+       depends on USB && VIDEO_DEV
+       ---help---
+         Say Y here if you want support for cameras based on SONiX SN9C101,
+         SN9C102 or SN9C103 PC Camera Controllers.
+
+         See <file:Documentation/video4linux/sn9c102.txt> for more info.
+
+         To compile this driver as a module, choose M here: the
+         module will be called sn9c102.
index df195c9..1013b4d 100644 (file)
@@ -401,7 +401,7 @@ static void tuner_status(struct i2c_client *client)
        }
        tuner_info("Tuner mode:      %s\n", p);
        tuner_info("Frequency:       %lu.%02lu MHz\n", freq, freq_fraction);
-       tuner_info("Standard:        0x%08llx\n", t->std);
+       tuner_info("Standard:        0x%08lx\n", (unsigned long)t->std);
        if (t->mode != V4L2_TUNER_RADIO)
               return;
        if (t->has_signal) {
@@ -558,10 +558,10 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode,
 
 static inline int check_v4l2(struct tuner *t)
 {
-       if (t->using_v4l2) {
-               tuner_dbg ("ignore v4l1 call\n");
-               return EINVAL;
-       }
+       /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
+          TV, v4l1 for radio), until that is fixed this code is disabled.
+          Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
+          first. */
        return 0;
 }
 
@@ -744,6 +744,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
                        switch_v4l2();
 
                        tuner->type = t->mode;
+                       if (t->mode == V4L2_TUNER_ANALOG_TV)
+                               tuner->capability |= V4L2_TUNER_CAP_NORM;
                        if (t->mode != V4L2_TUNER_RADIO) {
                                tuner->rangelow = tv_range[0] * 16;
                                tuner->rangehigh = tv_range[1] * 16;
index 356bff4..c2b7561 100644 (file)
@@ -1706,21 +1706,6 @@ static int chip_command(struct i2c_client *client,
                break;
        }
 
-       case VIDIOC_S_AUDIO:
-       {
-               struct v4l2_audio *sarg = arg;
-
-               if (!(desc->flags & CHIP_HAS_INPUTSEL) || sarg->index >= 4)
-                               return -EINVAL;
-               /* There are four inputs: tuner, radio, extern and intern. */
-               chip->input = sarg->index;
-               if (chip->muted)
-                       break;
-               chip_write_masked(chip, desc->inputreg,
-                               desc->inputmap[chip->input], desc->inputmask);
-               break;
-       }
-
        case VIDIOC_S_TUNER:
        {
                struct v4l2_tuner *vt = arg;
index e0d2ff8..431c3e2 100644 (file)
@@ -757,9 +757,9 @@ tveeprom_detect_client(struct i2c_adapter *adapter,
 static int
 tveeprom_attach_adapter (struct i2c_adapter *adapter)
 {
-       if (adapter->id != I2C_HW_B_BT848)
-               return 0;
-       return i2c_probe(adapter, &addr_data, tveeprom_detect_client);
+       if (adapter->class & I2C_CLASS_TV_ANALOG)
+               return i2c_probe(adapter, &addr_data, tveeprom_detect_client);
+       return 0;
 }
 
 static int
index 69d0fe1..dab4973 100644 (file)
@@ -53,7 +53,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = {
                .minimum = 0,
                .maximum = 255,
                .step = 1,
-               .default_value = 0,
+               .default_value = 128,
                .flags = 0,
        }, {
                .id = V4L2_CID_CONTRAST,
@@ -62,7 +62,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = {
                .minimum = 0,
                .maximum = 255,
                .step = 0x1,
-               .default_value = 0x10,
+               .default_value = 128,
                .flags = 0,
        }, {
                 .id = V4L2_CID_SATURATION,
@@ -71,7 +71,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = {
                 .minimum = 0,
                 .maximum = 255,
                 .step = 0x1,
-                .default_value = 0x10,
+                .default_value = 128,
                 .flags = 0,
        }, {
                .id = V4L2_CID_HUE,
@@ -80,7 +80,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = {
                .minimum = -128,
                .maximum = 127,
                .step = 0x1,
-               .default_value = 0x10,
+               .default_value = 0,
                .flags = 0,
        }
 };
@@ -500,16 +500,21 @@ struct i2c_vbi_ram_value {
 
 static struct i2c_vbi_ram_value vbi_ram_default[] =
 {
+       /* FIXME: Current api doesn't handle all VBI types, those not
+          yet supported are placed under #if 0 */
+#if 0
        {0x010, /* Teletext, SECAM, WST System A */
                {V4L2_SLICED_TELETEXT_SECAM,6,23,1},
                { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26,
                  0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 }
        },
+#endif
        {0x030, /* Teletext, PAL, WST System B */
-               {V4L2_SLICED_TELETEXT_PAL_B,6,22,1},
+               {V4L2_SLICED_TELETEXT_B,6,22,1},
                { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b,
                  0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 }
        },
+#if 0
        {0x050, /* Teletext, PAL, WST System C */
                {V4L2_SLICED_TELETEXT_PAL_C,6,22,1},
                { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
@@ -535,6 +540,7 @@ static struct i2c_vbi_ram_value vbi_ram_default[] =
                { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
                  0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
        },
+#endif
        {0x0f0, /* Closed Caption, NTSC */
                {V4L2_SLICED_CAPTION_525,21,21,1},
                { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
@@ -545,6 +551,7 @@ static struct i2c_vbi_ram_value vbi_ram_default[] =
                { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42,
                  0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 }
        },
+#if 0
        {0x130, /* Wide Screen Signal, NTSC C */
                {V4L2_SLICED_WSS_525,20,20,1},
                { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43,
@@ -560,6 +567,7 @@ static struct i2c_vbi_ram_value vbi_ram_default[] =
                { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
                  0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
        },
+#endif
        {0x190, /* Video Program System (VPS), PAL */
                {V4L2_SLICED_VPS,16,16,0},
                { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d,
@@ -850,7 +858,6 @@ static int tvp5150_command(struct i2c_client *c,
 
        case 0:
        case VIDIOC_INT_RESET:
-       case DECODER_INIT:
                tvp5150_reset(c);
                break;
        case VIDIOC_S_STD:
@@ -949,99 +956,15 @@ static int tvp5150_command(struct i2c_client *c,
 #endif
 
        case VIDIOC_LOG_STATUS:
-       case DECODER_DUMP:
                dump_reg(c);
                break;
 
-       case DECODER_GET_CAPABILITIES:
+       case VIDIOC_G_TUNER:
                {
-                       struct video_decoder_capability *cap = arg;
-
-                       cap->flags = VIDEO_DECODER_PAL |
-                           VIDEO_DECODER_NTSC |
-                           VIDEO_DECODER_SECAM |
-                           VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR;
-                       cap->inputs = 3;
-                       cap->outputs = 1;
-                       break;
-               }
-       case DECODER_GET_STATUS:
-               {
-                       int *iarg = arg;
-                       int status;
-                       int res=0;
-                       status = tvp5150_read(c, 0x88);
-                       if(status&0x08){
-                               res |= DECODER_STATUS_COLOR;
-                       }
-                       if(status&0x04 && status&0x02){
-                               res |= DECODER_STATUS_GOOD;
-                       }
-                       *iarg=res;
-                       break;
-               }
-
-       case DECODER_SET_GPIO:
-               break;
-
-       case DECODER_SET_VBI_BYPASS:
-               break;
-
-       case DECODER_SET_NORM:
-               {
-                       int *iarg = arg;
-
-                       switch (*iarg) {
-
-                       case VIDEO_MODE_NTSC:
-                               break;
-
-                       case VIDEO_MODE_PAL:
-                               break;
-
-                       case VIDEO_MODE_SECAM:
-                               break;
-
-                       case VIDEO_MODE_AUTO:
-                               break;
-
-                       default:
-                               return -EINVAL;
-
-                       }
-                       decoder->norm = *iarg;
-                       break;
-               }
-       case DECODER_SET_INPUT:
-               {
-                       int *iarg = arg;
-                       if (*iarg < 0 || *iarg > 3) {
-                               return -EINVAL;
-                       }
-
-                       decoder->input = *iarg;
-                       tvp5150_selmux(c, decoder->input);
-
-                       break;
-               }
-       case DECODER_SET_OUTPUT:
-               {
-                       int *iarg = arg;
-
-                       /* not much choice of outputs */
-                       if (*iarg != 0) {
-                               return -EINVAL;
-                       }
-                       break;
-               }
-       case DECODER_ENABLE_OUTPUT:
-               {
-                       int *iarg = arg;
-
-                       decoder->enable = (*iarg != 0);
-
-                       tvp5150_selmux(c, decoder->input);
+                       struct v4l2_tuner *vt = arg;
+                       int status = tvp5150_read(c, 0x88);
 
+                       vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
                        break;
                }
        case VIDIOC_QUERYCTRL:
@@ -1087,35 +1010,6 @@ static int tvp5150_command(struct i2c_client *c,
                        return -EINVAL;
                }
 
-       case DECODER_SET_PICTURE:
-               {
-                       struct video_picture *pic = arg;
-                       if (decoder->bright != pic->brightness) {
-                               /* We want 0 to 255 we get 0-65535 */
-                               decoder->bright = pic->brightness;
-                               tvp5150_write(c, TVP5150_BRIGHT_CTL,
-                                             decoder->bright >> 8);
-                       }
-                       if (decoder->contrast != pic->contrast) {
-                               /* We want 0 to 255 we get 0-65535 */
-                               decoder->contrast = pic->contrast;
-                               tvp5150_write(c, TVP5150_CONTRAST_CTL,
-                                             decoder->contrast >> 8);
-                       }
-                       if (decoder->sat != pic->colour) {
-                               /* We want 0 to 255 we get 0-65535 */
-                               decoder->sat = pic->colour;
-                               tvp5150_write(c, TVP5150_SATURATION_CTL,
-                                             decoder->contrast >> 8);
-                       }
-                       if (decoder->hue != pic->hue) {
-                               /* We want -128 to 127 we get 0-65535 */
-                               decoder->hue = pic->hue;
-                               tvp5150_write(c, TVP5150_HUE_CTL,
-                                             (decoder->hue - 32768) >> 8);
-                       }
-                       break;
-               }
        default:
                return -EINVAL;
        }
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
new file mode 100644 (file)
index 0000000..fc52201
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan
+ *
+ * 2003 by T.Adachi <tadachi@tadachi-net.com>
+ * 2003 by Takeru KOMORIYA <komoriya@paken.org>
+ * 2006 by Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/upd64031a.h>
+
+// --------------------- read registers functions define -----------------------
+
+/* bit masks */
+#define GR_MODE_MASK              0xc0
+#define DIRECT_3DYCS_CONNECT_MASK 0xc0
+#define SYNC_CIRCUIT_MASK         0xa0
+
+// -----------------------------------------------------------------------------
+
+MODULE_DESCRIPTION("uPD64031A driver");
+MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+static unsigned short normal_i2c[] = { 0x24 >> 1, 0x26 >> 1, I2C_CLIENT_END };
+
+
+I2C_CLIENT_INSMOD;
+
+enum {
+       R00 = 0, R01, R02, R03, R04,
+       R05, R06, R07, R08, R09,
+       R0A, R0B, R0C, R0D, R0E, R0F,
+       /* unused registers
+        R10, R11, R12, R13, R14,
+        R15, R16, R17,
+        */
+       TOT_REGS
+};
+
+struct upd64031a_state {
+       u8 regs[TOT_REGS];
+       u8 gr_mode;
+       u8 direct_3dycs_connect;
+       u8 ext_comp_sync;
+       u8 ext_vert_sync;
+};
+
+static u8 upd64031a_init[] = {
+       0x00, 0xb8, 0x48, 0xd2, 0xe6,
+       0x03, 0x10, 0x0b, 0xaf, 0x7f,
+       0x00, 0x00, 0x1d, 0x5e, 0x00,
+       0xd0
+};
+
+/* ------------------------------------------------------------------------ */
+
+static u8 upd64031a_read(struct i2c_client *client, u8 reg)
+{
+       u8 buf[2];
+
+       if (reg >= sizeof(buf))
+               return 0xff;
+       i2c_master_recv(client, buf, 2);
+       return buf[reg];
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void upd64031a_write(struct i2c_client *client, u8 reg, u8 val)
+{
+       u8 buf[2];
+
+       buf[0] = reg;
+       buf[1] = val;
+       v4l_dbg(1, debug, client, "writing reg addr: %02X val: %02X\n", reg, val);
+       if (i2c_master_send(client, buf, 2) != 2)
+               v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val);
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* The input changed due to new input or channel changed */
+static void upd64031a_change(struct i2c_client *client)
+{
+       struct upd64031a_state *state = i2c_get_clientdata(client);
+       u8 reg = state->regs[R00];
+
+       v4l_dbg(1, debug, client, "changed input or channel\n");
+       upd64031a_write(client, R00, reg | 0x10);
+       upd64031a_write(client, R00, reg & ~0x10);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+       struct upd64031a_state *state = i2c_get_clientdata(client);
+       struct v4l2_routing *route = arg;
+
+       switch (cmd) {
+       case VIDIOC_S_FREQUENCY:
+               upd64031a_change(client);
+               break;
+
+       case VIDIOC_INT_G_VIDEO_ROUTING:
+               route->input = (state->gr_mode >> 6) |
+                       (state->direct_3dycs_connect >> 4) |
+                       (state->ext_comp_sync >> 1) |
+                       (state->ext_vert_sync >> 2);
+               route->output = 0;
+               break;
+
+       case VIDIOC_INT_S_VIDEO_ROUTING:
+       {
+               u8 r00, r05, r08;
+
+               state->gr_mode = (route->input & 3) << 6;
+               state->direct_3dycs_connect = (route->input & 0xc) << 4;
+               state->ext_comp_sync = (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
+               state->ext_vert_sync = (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2;
+               r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode;
+               r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) |
+                       state->ext_comp_sync | state->ext_vert_sync;
+               r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) |
+                       state->direct_3dycs_connect;
+               upd64031a_write(client, R00, r00);
+               upd64031a_write(client, R05, r05);
+               upd64031a_write(client, R08, r08);
+               upd64031a_change(client);
+               break;
+       }
+
+       case VIDIOC_LOG_STATUS:
+               v4l_info(client, "Status: SA00=0x%02x SA01=0x%02x\n",
+                       upd64031a_read(client, 0), upd64031a_read(client, 1));
+               break;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       case VIDIOC_INT_G_REGISTER:
+       {
+               struct v4l2_register *reg = arg;
+
+               if (reg->i2c_id != I2C_DRIVERID_UPD64031A)
+                       return -EINVAL;
+               reg->val = upd64031a_read(client, reg->reg & 0xff);
+               break;
+       }
+
+       case VIDIOC_INT_S_REGISTER:
+       {
+               struct v4l2_register *reg = arg;
+               u8 addr = reg->reg & 0xff;
+               u8 val = reg->val & 0xff;
+
+               if (reg->i2c_id != I2C_DRIVERID_UPD64031A)
+                       return -EINVAL;
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+               upd64031a_write(client, addr, val);
+               break;
+       }
+#endif
+
+       default:
+               break;
+       }
+       return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* i2c implementation */
+
+static struct i2c_driver i2c_driver;
+
+static int upd64031a_attach(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *client;
+       struct upd64031a_state *state;
+       int i;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return 0;
+
+       client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+       if (client == NULL) {
+               return -ENOMEM;
+       }
+
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &i2c_driver;
+       snprintf(client->name, sizeof(client->name) - 1, "uPD64031A");
+
+       v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+
+       state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
+       if (state == NULL) {
+               kfree(client);
+               return -ENOMEM;
+       }
+       i2c_set_clientdata(client, state);
+       memcpy(state->regs, upd64031a_init, sizeof(state->regs));
+       state->gr_mode = UPD64031A_GR_ON << 6;
+       state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4;
+       state->ext_comp_sync = state->ext_vert_sync = 0;
+       for (i = 0; i < TOT_REGS; i++) {
+               upd64031a_write(client, i, state->regs[i]);
+       }
+
+       i2c_attach_client(client);
+
+       return 0;
+}
+
+static int upd64031a_probe(struct i2c_adapter *adapter)
+{
+       if (adapter->class & I2C_CLASS_TV_ANALOG)
+               return i2c_probe(adapter, &addr_data, upd64031a_attach);
+       return 0;
+}
+
+static int upd64031a_detach(struct i2c_client *client)
+{
+       int err;
+
+       err = i2c_detach_client(client);
+       if (err)
+               return err;
+
+       kfree(client);
+       return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+static struct i2c_driver i2c_driver = {
+       .driver = {
+               .name = "upd64031a",
+       },
+       .id = I2C_DRIVERID_UPD64031A,
+       .attach_adapter = upd64031a_probe,
+       .detach_client  = upd64031a_detach,
+       .command = upd64031a_command,
+};
+
+
+static int __init upd64031a_init_module(void)
+{
+       return i2c_add_driver(&i2c_driver);
+}
+
+static void __exit upd64031a_exit_module(void)
+{
+       i2c_del_driver(&i2c_driver);
+}
+
+module_init(upd64031a_init_module);
+module_exit(upd64031a_exit_module);
diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
new file mode 100644 (file)
index 0000000..c3a7ffe
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver
+ *
+ * 2003 by T.Adachi (tadachi@tadachi-net.com)
+ * 2003 by Takeru KOMORIYA <komoriya@paken.org>
+ * 2006 by Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/upd64083.h>
+
+MODULE_DESCRIPTION("uPD64083 driver");
+MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+module_param(debug, bool, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+static unsigned short normal_i2c[] = { 0xb8 >> 1, 0xba >> 1, I2C_CLIENT_END };
+
+
+I2C_CLIENT_INSMOD;
+
+enum {
+       R00 = 0, R01, R02, R03, R04,
+       R05, R06, R07, R08, R09,
+       R0A, R0B, R0C, R0D, R0E, R0F,
+       R10, R11, R12, R13, R14,
+       R15, R16,
+       TOT_REGS
+};
+
+struct upd64083_state {
+       u8 mode;
+       u8 ext_y_adc;
+       u8 regs[TOT_REGS];
+};
+
+/* Initial values when used in combination with the
+   NEC upd64031a ghost reduction chip. */
+static u8 upd64083_init[] = {
+       0x1f, 0x01, 0xa0, 0x2d, 0x29,  /* we use EXCSS=0 */
+       0x36, 0xdd, 0x05, 0x56, 0x48,
+       0x00, 0x3a, 0xa0, 0x05, 0x08,
+       0x44, 0x60, 0x08, 0x52, 0xf8,
+       0x53, 0x60, 0x10
+};
+
+/* ------------------------------------------------------------------------ */
+
+static void upd64083_log_status(struct i2c_client *client)
+{
+       u8 buf[7];
+
+       i2c_master_recv(client, buf, 7);
+       v4l_info(client, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x "
+                     "SA04=%02x SA05=%02x SA06=%02x\n",
+               buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void upd64083_write(struct i2c_client *client, u8 reg, u8 val)
+{
+       u8 buf[2];
+
+       buf[0] = reg;
+       buf[1] = val;
+       v4l_dbg(1, debug, client, "writing reg addr: %02x val: %02x\n", reg, val);
+       if (i2c_master_send(client, buf, 2) != 2)
+               v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val);
+}
+
+/* ------------------------------------------------------------------------ */
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static u8 upd64083_read(struct i2c_client *client, u8 reg)
+{
+       u8 buf[7];
+
+       if (reg >= sizeof(buf))
+               return 0xff;
+       i2c_master_recv(client, buf, sizeof(buf));
+       return buf[reg];
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+       struct upd64083_state *state = i2c_get_clientdata(client);
+       struct v4l2_routing *route = arg;
+
+       switch (cmd) {
+       case VIDIOC_INT_G_VIDEO_ROUTING:
+               route->input = (state->mode >> 6) | (state->ext_y_adc >> 3);
+               route->output = 0;
+               break;
+
+       case VIDIOC_INT_S_VIDEO_ROUTING:
+       {
+               u8 r00, r02;
+
+               if (route->input > 7 || (route->input & 6) == 6)
+                       return -EINVAL;
+               state->mode = (route->input & 3) << 6;
+               state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3;
+               r00 = (state->regs[R00] & ~(3 << 6)) | state->mode;
+               r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc;
+               upd64083_write(client, R00, r00);
+               upd64083_write(client, R02, r02);
+               break;
+       }
+
+       case VIDIOC_LOG_STATUS:
+               upd64083_log_status(client);
+               break;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       case VIDIOC_INT_G_REGISTER:
+       {
+               struct v4l2_register *reg = arg;
+
+               if (reg->i2c_id != I2C_DRIVERID_UPD64083)
+                       return -EINVAL;
+               reg->val = upd64083_read(client, reg->reg & 0xff);
+               break;
+       }
+
+       case VIDIOC_INT_S_REGISTER:
+       {
+               struct v4l2_register *reg = arg;
+               u8 addr = reg->reg & 0xff;
+               u8 val = reg->val & 0xff;
+
+               if (reg->i2c_id != I2C_DRIVERID_UPD64083)
+                       return -EINVAL;
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+               upd64083_write(client, addr, val);
+               break;
+       }
+#endif
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* i2c implementation */
+
+static struct i2c_driver i2c_driver;
+
+static int upd64083_attach(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *client;
+       struct upd64083_state *state;
+       int i;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return 0;
+
+       client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+       if (client == NULL) {
+               return -ENOMEM;
+       }
+
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &i2c_driver;
+       snprintf(client->name, sizeof(client->name) - 1, "uPD64083");
+
+       v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+
+       state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL);
+       if (state == NULL) {
+               kfree(client);
+               return -ENOMEM;
+       }
+       i2c_set_clientdata(client, state);
+       /* Initially assume that a ghost reduction chip is present */
+       state->mode = 0;  /* YCS mode */
+       state->ext_y_adc = (1 << 5);
+       memcpy(state->regs, upd64083_init, TOT_REGS);
+       for (i = 0; i < TOT_REGS; i++) {
+               upd64083_write(client, i, state->regs[i]);
+       }
+       i2c_attach_client(client);
+
+       return 0;
+}
+
+static int upd64083_probe(struct i2c_adapter *adapter)
+{
+       if (adapter->class & I2C_CLASS_TV_ANALOG)
+               return i2c_probe(adapter, &addr_data, upd64083_attach);
+       return 0;
+}
+
+static int upd64083_detach(struct i2c_client *client)
+{
+       int err;
+
+       err = i2c_detach_client(client);
+       if (err)
+               return err;
+
+       kfree(client);
+       return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+static struct i2c_driver i2c_driver = {
+       .driver = {
+               .name = "upd64083",
+       },
+       .id = I2C_DRIVERID_UPD64083,
+       .attach_adapter = upd64083_probe,
+       .detach_client  = upd64083_detach,
+       .command = upd64083_command,
+};
+
+
+static int __init upd64083_init_module(void)
+{
+       return i2c_add_driver(&i2c_driver);
+}
+
+static void __exit upd64083_exit_module(void)
+{
+       i2c_del_driver(&i2c_driver);
+}
+
+module_init(upd64083_init_module);
+module_exit(upd64083_exit_module);
diff --git a/drivers/media/video/usbvideo/Kconfig b/drivers/media/video/usbvideo/Kconfig
new file mode 100644 (file)
index 0000000..08a5d20
--- /dev/null
@@ -0,0 +1,38 @@
+config VIDEO_USBVIDEO
+       tristate
+
+config USB_VICAM
+       tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)"
+       depends on USB && VIDEO_DEV && EXPERIMENTAL
+       select VIDEO_USBVIDEO
+       ---help---
+         Say Y here if you have 3com homeconnect camera (vicam).
+
+         To compile this driver as a module, choose M here: the
+         module will be called vicam.
+
+config USB_IBMCAM
+       tristate "USB IBM (Xirlink) C-it Camera support"
+       depends on USB && VIDEO_DEV
+       select VIDEO_USBVIDEO
+       ---help---
+         Say Y here if you want to connect a IBM "C-It" camera, also known as
+         "Xirlink PC Camera" to your computer's USB port.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ibmcam.
+
+         This camera has several configuration options which
+         can be specified when you load the module. Read
+         <file:Documentation/video4linux/ibmcam.txt> to learn more.
+
+config USB_KONICAWC
+       tristate "USB Konica Webcam support"
+       depends on USB && VIDEO_DEV
+       select VIDEO_USBVIDEO
+       ---help---
+         Say Y here if you want support for webcams based on a Konica
+         chipset. This is known to work with the Intel YC76 webcam.
+
+         To compile this driver as a module, choose M here: the
+         module will be called konicawc.
index ed410a5..bb52eb8 100644 (file)
@@ -1,4 +1,4 @@
-obj-$(CONFIG_USB_IBMCAM)        += ibmcam.o usbvideo.o ultracam.o
-obj-$(CONFIG_USB_KONICAWC)      += konicawc.o usbvideo.o
-obj-$(CONFIG_USB_VICAM)         += vicam.o usbvideo.o
-
+obj-$(CONFIG_VIDEO_USBVIDEO)    += usbvideo.o
+obj-$(CONFIG_USB_IBMCAM)        += ibmcam.o ultracam.o
+obj-$(CONFIG_USB_KONICAWC)      += konicawc.o
+obj-$(CONFIG_USB_VICAM)         += vicam.o
index 11a97f3..d330fa9 100644 (file)
@@ -317,6 +317,7 @@ static const char *v4l2_int_ioctls[] = {
        [_IOC_NR(TUNER_SET_STANDBY)]           = "TUNER_SET_STANDBY",
        [_IOC_NR(TDA9887_SET_CONFIG)]          = "TDA9887_SET_CONFIG",
 
+       [_IOC_NR(VIDIOC_INT_S_TUNER_MODE)]     = "VIDIOC_INT_S_TUNER_MODE",
        [_IOC_NR(VIDIOC_INT_S_REGISTER)]       = "VIDIOC_INT_S_REGISTER",
        [_IOC_NR(VIDIOC_INT_G_REGISTER)]       = "VIDIOC_INT_G_REGISTER",
        [_IOC_NR(VIDIOC_INT_RESET)]            = "VIDIOC_INT_RESET",
@@ -325,7 +326,12 @@ static const char *v4l2_int_ioctls[] = {
        [_IOC_NR(VIDIOC_INT_S_VBI_DATA)]       = "VIDIOC_INT_S_VBI_DATA",
        [_IOC_NR(VIDIOC_INT_G_VBI_DATA)]       = "VIDIOC_INT_G_VBI_DATA",
        [_IOC_NR(VIDIOC_INT_G_CHIP_IDENT)]     = "VIDIOC_INT_G_CHIP_IDENT",
-       [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)]   = "VIDIOC_INT_I2S_CLOCK_FREQ"
+       [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)]   = "VIDIOC_INT_I2S_CLOCK_FREQ",
+       [_IOC_NR(VIDIOC_INT_S_STANDBY)]        = "VIDIOC_INT_S_STANDBY",
+       [_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)]  = "VIDIOC_INT_S_AUDIO_ROUTING",
+       [_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)]  = "VIDIOC_INT_G_AUDIO_ROUTING",
+       [_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)]  = "VIDIOC_INT_S_VIDEO_ROUTING",
+       [_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)]  = "VIDIOC_INT_G_VIDEO_ROUTING"
 };
 #define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls)
 
index d2ca0f0..acc5ea9 100644 (file)
@@ -399,19 +399,25 @@ void videobuf_queue_pci(struct videobuf_queue* q)
 int videobuf_pci_dma_map(struct pci_dev *pci,struct videobuf_dmabuf *dma)
 {
        struct videobuf_queue q;
+       struct videobuf_queue_ops qops;
 
        q.dev=pci;
-       q.ops->vb_map_sg=(vb_map_sg_t *)pci_unmap_sg;
+       qops.vb_map_sg=(vb_map_sg_t *)pci_map_sg;
+       qops.vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg;
+       q.ops = &qops;
 
-       return (videobuf_dma_unmap(&q,dma));
+       return (videobuf_dma_map(&q,dma));
 }
 
 int videobuf_pci_dma_unmap(struct pci_dev *pci,struct videobuf_dmabuf *dma)
 {
        struct videobuf_queue q;
+       struct videobuf_queue_ops qops;
 
        q.dev=pci;
-       q.ops->vb_map_sg=(vb_map_sg_t *)pci_unmap_sg;
+       qops.vb_map_sg=(vb_map_sg_t *)pci_map_sg;
+       qops.vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg;
+       q.ops = &qops;
 
        return (videobuf_dma_unmap(&q,dma));
 }
@@ -923,7 +929,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
                /* need to capture a new frame */
                retval = -ENOMEM;
                q->read_buf = videobuf_alloc(q->msize);
-               dprintk(1,"video alloc=0x%08x\n",(unsigned int) q->read_buf);
+               dprintk(1,"video alloc=0x%p\n", q->read_buf);
                if (NULL == q->read_buf)
                        goto done;
                q->read_buf->memory = V4L2_MEMORY_USERPTR;
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
new file mode 100644 (file)
index 0000000..a9b59c3
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * wm8739
+ *
+ * Copyright (C) 2005 T. Adachi <tadachi@tadachi-net.com>
+ *
+ * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl>
+ * - Cleanup
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/videodev.h>
+#include <media/v4l2-common.h>
+
+MODULE_DESCRIPTION("wm8739 driver");
+MODULE_AUTHOR("T. Adachi, Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+static unsigned short normal_i2c[] = { 0x34 >> 1, 0x36 >> 1, I2C_CLIENT_END };
+
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+I2C_CLIENT_INSMOD;
+
+/* ------------------------------------------------------------------------ */
+
+enum {
+       R0 = 0, R1,
+       R5 = 5, R6, R7, R8, R9, R15 = 15,
+       TOT_REGS
+};
+
+struct wm8739_state {
+       u32 clock_freq;
+       u8 muted;
+       u16 volume;
+       u16 balance;
+       u8 vol_l;               /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
+       u8 vol_r;               /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
+};
+
+/* ------------------------------------------------------------------------ */
+
+static int wm8739_write(struct i2c_client *client, int reg, u16 val)
+{
+       int i;
+
+       if (reg < 0 || reg >= TOT_REGS) {
+               v4l_err(client, "Invalid register R%d\n", reg);
+               return -1;
+       }
+
+       v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val);
+
+       for (i = 0; i < 3; i++) {
+               if (i2c_smbus_write_byte_data(client, (reg << 1) |
+                                       (val >> 8), val & 0xff) == 0) {
+                       return 0;
+               }
+       }
+       v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
+       return -1;
+}
+
+/* write regs to set audio volume etc */
+static void wm8739_set_audio(struct i2c_client *client)
+{
+       struct wm8739_state *state = i2c_get_clientdata(client);
+       u16 mute = state->muted ? 0x80 : 0;
+
+       /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
+        * Default setting: 0x17 = 0 dB
+        */
+       wm8739_write(client, R0, (state->vol_l & 0x1f) | mute);
+       wm8739_write(client, R1, (state->vol_r & 0x1f) | mute);
+}
+
+static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+       struct wm8739_state *state = i2c_get_clientdata(client);
+
+       switch (ctrl->id) {
+       case V4L2_CID_AUDIO_MUTE:
+               ctrl->value = state->muted;
+               break;
+
+       case V4L2_CID_AUDIO_VOLUME:
+               ctrl->value = state->volume;
+               break;
+
+       case V4L2_CID_AUDIO_BALANCE:
+               ctrl->value = state->balance;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+       struct wm8739_state *state = i2c_get_clientdata(client);
+       unsigned int work_l, work_r;
+
+       switch (ctrl->id) {
+       case V4L2_CID_AUDIO_MUTE:
+               state->muted = ctrl->value;
+               break;
+
+       case V4L2_CID_AUDIO_VOLUME:
+               state->volume = ctrl->value;
+               break;
+
+       case V4L2_CID_AUDIO_BALANCE:
+               state->balance = ctrl->value;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */
+       work_l = (min(65536 - state->balance, 32768) * state->volume) / 32768;
+       work_r = (min(state->balance, (u16)32768) * state->volume) / 32768;
+
+       state->vol_l = (long)work_l * 31 / 65535;
+       state->vol_r = (long)work_r * 31 / 65535;
+
+       /* set audio volume etc. */
+       wm8739_set_audio(client);
+       return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct v4l2_queryctrl wm8739_qctrl[] = {
+       {
+               .id            = V4L2_CID_AUDIO_VOLUME,
+               .name          = "Volume",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 65535/100,
+               .default_value = 58880,
+               .flags         = 0,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_AUDIO_MUTE,
+               .name          = "Mute",
+               .minimum       = 0,
+               .maximum       = 1,
+               .step          = 1,
+               .default_value = 1,
+               .flags         = 0,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_AUDIO_BALANCE,
+               .name          = "Balance",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 65535/100,
+               .default_value = 32768,
+               .flags         = 0,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       }
+};
+
+/* ------------------------------------------------------------------------ */
+
+static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+       struct wm8739_state *state = i2c_get_clientdata(client);
+
+       switch (cmd) {
+       case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+       {
+               u32 audiofreq = *(u32 *)arg;
+
+               state->clock_freq = audiofreq;
+               wm8739_write(client, R9, 0x000);        /* de-activate */
+               switch (audiofreq) {
+               case 44100:
+                       wm8739_write(client, R8, 0x020); /* 256fps, fs=44.1k     */
+                       break;
+               case 48000:
+                       wm8739_write(client, R8, 0x000); /* 256fps, fs=48k       */
+                       break;
+               case 32000:
+                       wm8739_write(client, R8, 0x018); /* 256fps, fs=32k       */
+                       break;
+               default:
+                       break;
+               }
+               wm8739_write(client, R9, 0x001);        /* activate */
+               break;
+       }
+
+       case VIDIOC_G_CTRL:
+               return wm8739_get_ctrl(client, arg);
+
+       case VIDIOC_S_CTRL:
+               return wm8739_set_ctrl(client, arg);
+
+       case VIDIOC_QUERYCTRL:
+       {
+               struct v4l2_queryctrl *qc = arg;
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
+                       if (qc->id && qc->id == wm8739_qctrl[i].id) {
+                               memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
+                               return 0;
+                       }
+               return -EINVAL;
+       }
+
+       case VIDIOC_LOG_STATUS:
+               v4l_info(client, "Frequency: %u Hz\n", state->clock_freq);
+               v4l_info(client, "Volume L:  %02x%s\n", state->vol_l & 0x1f,
+                               state->muted ? " (muted)" : "");
+               v4l_info(client, "Volume R:  %02x%s\n", state->vol_r & 0x1f,
+                               state->muted ? " (muted)" : "");
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* i2c implementation */
+
+static struct i2c_driver i2c_driver;
+
+static int wm8739_attach(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *client;
+       struct wm8739_state *state;
+
+       /* Check if the adapter supports the needed features */
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return 0;
+
+       client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+       if (client == NULL)
+               return -ENOMEM;
+
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &i2c_driver;
+       snprintf(client->name, sizeof(client->name) - 1, "wm8739");
+
+       v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+
+       state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL);
+       if (state == NULL) {
+               kfree(client);
+               return -ENOMEM;
+       }
+       state->vol_l = 0x17; /* 0dB */
+       state->vol_r = 0x17; /* 0dB */
+       state->muted = 0;
+       state->balance = 32768;
+       /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */
+       state->volume = ((long)state->vol_l + 1) * 65535 / 31;
+       state->clock_freq = 48000;
+       i2c_set_clientdata(client, state);
+
+       /* initialize wm8739 */
+       wm8739_write(client, R15, 0x00); /* reset */
+       wm8739_write(client, R5, 0x000); /* filter setting, high path, offet clear */
+       wm8739_write(client, R6, 0x000); /* ADC, OSC, Power Off mode Disable */
+       wm8739_write(client, R7, 0x049); /* Digital Audio interface format */
+                                        /* Enable Master mode */
+                                        /* 24 bit, MSB first/left justified */
+       wm8739_write(client, R8, 0x000); /* sampling control */
+                                        /* normal, 256fs, 48KHz sampling rate */
+       wm8739_write(client, R9, 0x001); /* activate */
+       wm8739_set_audio(client);        /* set volume/mute */
+
+       i2c_attach_client(client);
+
+       return 0;
+}
+
+static int wm8739_probe(struct i2c_adapter *adapter)
+{
+       if (adapter->class & I2C_CLASS_TV_ANALOG)
+               return i2c_probe(adapter, &addr_data, wm8739_attach);
+       return 0;
+}
+
+static int wm8739_detach(struct i2c_client *client)
+{
+       int err;
+
+       err = i2c_detach_client(client);
+       if (err)
+               return err;
+
+       kfree(client);
+       return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+static struct i2c_driver i2c_driver = {
+       .driver = {
+               .name = "wm8739",
+       },
+       .id = I2C_DRIVERID_WM8739,
+       .attach_adapter = wm8739_probe,
+       .detach_client  = wm8739_detach,
+       .command = wm8739_command,
+};
+
+
+static int __init wm8739_init_module(void)
+{
+       return i2c_add_driver(&i2c_driver);
+}
+
+static void __exit wm8739_cleanup_module(void)
+{
+       i2c_del_driver(&i2c_driver);
+}
+
+module_init(wm8739_init_module);
+module_exit(wm8739_cleanup_module);
diff --git a/drivers/media/video/zc0301/Kconfig b/drivers/media/video/zc0301/Kconfig
new file mode 100644 (file)
index 0000000..c3bf886
--- /dev/null
@@ -0,0 +1,11 @@
+config USB_ZC0301
+       tristate "USB ZC0301 Image Processor and Control Chip support"
+       depends on USB && VIDEO_DEV
+       ---help---
+         Say Y here if you want support for cameras based on the ZC0301
+         Image Processor and Control Chip.
+
+         See <file:Documentation/video4linux/zc0301.txt> for more info.
+
+         To compile this driver as a module, choose M here: the
+         module will be called zc0301.
index 0f6bb2e..a7ec595 100644 (file)
@@ -200,27 +200,6 @@ config MTD_CFI_AMDSTD
          provides support for one of those command sets, used on chips
          including the AMD Am29LV320.
 
-config MTD_CFI_AMDSTD_RETRY
-       int "Retry failed commands (erase/program)"
-       depends on MTD_CFI_AMDSTD
-       default "0"
-       help
-         Some chips, when attached to a shared bus, don't properly filter
-         bus traffic that is destined to other devices.  This broken
-         behavior causes erase and program sequences to be aborted when
-         the sequences are mixed with traffic for other devices.
-
-         SST49LF040 (and related) chips are know to be broken.
-
-config MTD_CFI_AMDSTD_RETRY_MAX
-       int "Max retries of failed commands (erase/program)"
-       depends on MTD_CFI_AMDSTD_RETRY
-       default "0"
-       help
-         If you have an SST49LF040 (or related chip) then this value should
-         be set to at least 1.  This can also be adjusted at driver load
-         time with the retry_cmd_max module parameter.
-
 config MTD_CFI_STAA
        tristate "Support for ST (Advanced Architecture) flash chips"
        depends on MTD_GEN_PROBE
index 8bbc751..d27f412 100644 (file)
@@ -54,7 +54,7 @@ static const int debug = 0;
 #define MAX_PCMCIA_ADDR        0x4000000
 
 struct pcmciamtd_dev {
-       dev_link_t      link;           /* PCMCIA link */
+       struct pcmcia_device    *p_dev;
        dev_node_t      node;           /* device node */
        caddr_t         win_base;       /* ioremapped address of PCMCIA window */
        unsigned int    win_size;       /* size of window */
@@ -111,8 +111,8 @@ static caddr_t remap_window(struct map_info *map, unsigned long to)
        memreq_t mrq;
        int ret;
 
-       if(!(dev->link.state & DEV_PRESENT)) {
-               DEBUG(1, "device removed state = 0x%4.4X", dev->link.state);
+       if (!pcmcia_dev_present(dev->p_dev)) {
+               DEBUG(1, "device removed");
                return 0;
        }
 
@@ -122,7 +122,7 @@ static caddr_t remap_window(struct map_info *map, unsigned long to)
                      dev->offset, mrq.CardOffset);
                mrq.Page = 0;
                if( (ret = pcmcia_map_mem_page(win, &mrq)) != CS_SUCCESS) {
-                       cs_error(dev->link.handle, MapMemPage, ret);
+                       cs_error(dev->p_dev, MapMemPage, ret);
                        return NULL;
                }
                dev->offset = mrq.CardOffset;
@@ -238,7 +238,7 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v
 
 /* read/write{8,16} copy_{from,to} routines with direct access */
 
-#define DEV_REMOVED(x)  (!(*(u_int *)x->map_priv_1 & DEV_PRESENT))
+#define DEV_REMOVED(x)  (!(pcmcia_dev_present(((struct pcmciamtd_dev *)map->map_priv_1)->p_dev)))
 
 static map_word pcmcia_read8(struct map_info *map, unsigned long ofs)
 {
@@ -319,7 +319,7 @@ static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *f
 static void pcmciamtd_set_vpp(struct map_info *map, int on)
 {
        struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
-       dev_link_t *link = &dev->link;
+       struct pcmcia_device *link = dev->p_dev;
        modconf_t mod;
        int ret;
 
@@ -328,9 +328,9 @@ static void pcmciamtd_set_vpp(struct map_info *map, int on)
        mod.Vpp1 = mod.Vpp2 = on ? dev->vpp : 0;
 
        DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp);
-       ret = pcmcia_modify_configuration(link->handle, &mod);
+       ret = pcmcia_modify_configuration(link, &mod);
        if(ret != CS_SUCCESS) {
-               cs_error(link->handle, ModifyConfiguration, ret);
+               cs_error(link, ModifyConfiguration, ret);
        }
 }
 
@@ -340,7 +340,7 @@ static void pcmciamtd_set_vpp(struct map_info *map, int on)
  * still open, this will be postponed until it is closed.
  */
 
-static void pcmciamtd_release(dev_link_t *link)
+static void pcmciamtd_release(struct pcmcia_device *link)
 {
        struct pcmciamtd_dev *dev = link->priv;
 
@@ -353,12 +353,11 @@ static void pcmciamtd_release(dev_link_t *link)
                }
                pcmcia_release_window(link->win);
        }
-       pcmcia_release_configuration(link->handle);
-       link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
 }
 
 
-static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_name)
+static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, int *new_name)
 {
        int rc;
        tuple_t tuple;
@@ -371,16 +370,16 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_
        tuple.TupleOffset = 0;
        tuple.DesiredTuple = RETURN_FIRST_TUPLE;
 
-       rc = pcmcia_get_first_tuple(link->handle, &tuple);
+       rc = pcmcia_get_first_tuple(link, &tuple);
        while(rc == CS_SUCCESS) {
-               rc = pcmcia_get_tuple_data(link->handle, &tuple);
+               rc = pcmcia_get_tuple_data(link, &tuple);
                if(rc != CS_SUCCESS) {
-                       cs_error(link->handle, GetTupleData, rc);
+                       cs_error(link, GetTupleData, rc);
                        break;
                }
-               rc = pcmcia_parse_tuple(link->handle, &tuple, &parse);
+               rc = pcmcia_parse_tuple(link, &tuple, &parse);
                if(rc != CS_SUCCESS) {
-                       cs_error(link->handle, ParseTuple, rc);
+                       cs_error(link, ParseTuple, rc);
                        break;
                }
 
@@ -451,7 +450,7 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_
                        DEBUG(2, "Unknown tuple code %d", tuple.TupleCode);
                }
 
-               rc = pcmcia_get_next_tuple(link->handle, &tuple);
+               rc = pcmcia_get_next_tuple(link, &tuple);
        }
        if(!dev->pcmcia_map.size)
                dev->pcmcia_map.size = MAX_PCMCIA_ADDR;
@@ -488,7 +487,7 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void pcmciamtd_config(dev_link_t *link)
+static int pcmciamtd_config(struct pcmcia_device *link)
 {
        struct pcmciamtd_dev *dev = link->priv;
        struct mtd_info *mtd = NULL;
@@ -504,13 +503,10 @@ static void pcmciamtd_config(dev_link_t *link)
 
        DEBUG(3, "link=0x%p", link);
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
        DEBUG(2, "Validating CIS");
-       ret = pcmcia_validate_cis(link->handle, &cisinfo);
+       ret = pcmcia_validate_cis(link, &cisinfo);
        if(ret != CS_SUCCESS) {
-               cs_error(link->handle, GetTupleData, ret);
+               cs_error(link, GetTupleData, ret);
        } else {
                DEBUG(2, "ValidateCIS found %d chains", cisinfo.Chains);
        }
@@ -538,7 +534,7 @@ static void pcmciamtd_config(dev_link_t *link)
        req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16;
        req.Base = 0;
        req.AccessSpeed = mem_speed;
-       link->win = (window_handle_t)link->handle;
+       link->win = (window_handle_t)link;
        req.Size = (force_size) ? force_size << 20 : MAX_PCMCIA_ADDR;
        dev->win_size = 0;
 
@@ -546,7 +542,7 @@ static void pcmciamtd_config(dev_link_t *link)
                int ret;
                DEBUG(2, "requesting window with size = %dKiB memspeed = %d",
                      req.Size >> 10, req.AccessSpeed);
-               ret = pcmcia_request_window(&link->handle, &req, &link->win);
+               ret = pcmcia_request_window(&link, &req, &link->win);
                DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size);
                if(ret) {
                        req.Size >>= 1;
@@ -562,19 +558,19 @@ static void pcmciamtd_config(dev_link_t *link)
        if(!dev->win_size) {
                err("Cant allocate memory window");
                pcmciamtd_release(link);
-               return;
+               return -ENODEV;
        }
        DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10);
 
        /* Get write protect status */
-       CS_CHECK(GetStatus, pcmcia_get_status(link->handle, &status));
+       CS_CHECK(GetStatus, pcmcia_get_status(link, &status));
        DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx",
              status.CardState, (unsigned long)link->win);
        dev->win_base = ioremap(req.Base, req.Size);
        if(!dev->win_base) {
                err("ioremap(%lu, %u) failed", req.Base, req.Size);
                pcmciamtd_release(link);
-               return;
+               return -ENODEV;
        }
        DEBUG(1, "mapped window dev = %p req.base = 0x%lx base = %p size = 0x%x",
              dev, req.Base, dev->win_base, req.Size);
@@ -584,17 +580,14 @@ static void pcmciamtd_config(dev_link_t *link)
        dev->pcmcia_map.map_priv_2 = (unsigned long)link->win;
 
        DEBUG(2, "Getting configuration");
-       CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link->handle, &t));
+       CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &t));
        DEBUG(2, "Vcc = %d Vpp1 = %d Vpp2 = %d", t.Vcc, t.Vpp1, t.Vpp2);
        dev->vpp = (vpp) ? vpp : t.Vpp1;
        link->conf.Attributes = 0;
-       link->conf.Vcc = t.Vcc;
        if(setvpp == 2) {
-               link->conf.Vpp1 = dev->vpp;
-               link->conf.Vpp2 = dev->vpp;
+               link->conf.Vpp = dev->vpp;
        } else {
-               link->conf.Vpp1 = 0;
-               link->conf.Vpp2 = 0;
+               link->conf.Vpp = 0;
        }
 
        link->conf.IntType = INT_MEMORY;
@@ -606,9 +599,10 @@ static void pcmciamtd_config(dev_link_t *link)
        link->conf.ConfigIndex = 0;
        link->conf.Present = t.Present;
        DEBUG(2, "Setting Configuration");
-       ret = pcmcia_request_configuration(link->handle, &link->conf);
+       ret = pcmcia_request_configuration(link, &link->conf);
        if(ret != CS_SUCCESS) {
-               cs_error(link->handle, RequestConfiguration, ret);
+               cs_error(link, RequestConfiguration, ret);
+               return -ENODEV;
        }
 
        if(mem_type == 1) {
@@ -629,7 +623,7 @@ static void pcmciamtd_config(dev_link_t *link)
        if(!mtd) {
                DEBUG(1, "Cant find an MTD");
                pcmciamtd_release(link);
-               return;
+               return -ENODEV;
        }
 
        dev->mtd_info = mtd;
@@ -654,7 +648,6 @@ static void pcmciamtd_config(dev_link_t *link)
           use the faster non-remapping read/write functions */
        if(mtd->size <= dev->win_size) {
                DEBUG(1, "Using non remapping memory functions");
-               dev->pcmcia_map.map_priv_1 = (unsigned long)&(dev->link.state);
                dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base;
                if (dev->pcmcia_map.bankwidth == 1) {
                        dev->pcmcia_map.read = pcmcia_read8;
@@ -672,19 +665,18 @@ static void pcmciamtd_config(dev_link_t *link)
                dev->mtd_info = NULL;
                err("Couldnt register MTD device");
                pcmciamtd_release(link);
-               return;
+               return -ENODEV;
        }
        snprintf(dev->node.dev_name, sizeof(dev->node.dev_name), "mtd%d", mtd->index);
        info("mtd%d: %s", mtd->index, mtd->name);
-       link->state &= ~DEV_CONFIG_PENDING;
-       link->dev = &dev->node;
-       return;
+       link->dev_node = &dev->node;
+       return 0;
 
  cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
        err("CS Error, exiting");
        pcmciamtd_release(link);
-       return;
+       return -ENODEV;
 }
 
 
@@ -713,21 +705,18 @@ static int pcmciamtd_resume(struct pcmcia_device *dev)
  * when the device is released.
  */
 
-static void pcmciamtd_detach(struct pcmcia_device *p_dev)
+static void pcmciamtd_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
+       struct pcmciamtd_dev *dev = link->priv;
 
        DEBUG(3, "link=0x%p", link);
 
-       if(link->state & DEV_CONFIG) {
-               struct pcmciamtd_dev *dev = link->priv;
-               if(dev->mtd_info) {
-                       del_mtd_device(dev->mtd_info);
-                       info("mtd%d: Removed", dev->mtd_info->index);
-               }
-
-               pcmciamtd_release(link);
+       if(dev->mtd_info) {
+               del_mtd_device(dev->mtd_info);
+               info("mtd%d: Removed", dev->mtd_info->index);
        }
+
+       pcmciamtd_release(link);
 }
 
 
@@ -736,10 +725,9 @@ static void pcmciamtd_detach(struct pcmcia_device *p_dev)
  * with Card Services.
  */
 
-static int pcmciamtd_attach(struct pcmcia_device *p_dev)
+static int pcmciamtd_probe(struct pcmcia_device *link)
 {
        struct pcmciamtd_dev *dev;
-       dev_link_t *link;
 
        /* Create new memory card device */
        dev = kmalloc(sizeof(*dev), GFP_KERNEL);
@@ -747,20 +735,13 @@ static int pcmciamtd_attach(struct pcmcia_device *p_dev)
        DEBUG(1, "dev=0x%p", dev);
 
        memset(dev, 0, sizeof(*dev));
-       link = &dev->link;
+       dev->p_dev = link;
        link->priv = dev;
 
        link->conf.Attributes = 0;
        link->conf.IntType = INT_MEMORY;
 
-       link->next = NULL;
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       pcmciamtd_config(link);
-
-       return 0;
+       return pcmciamtd_config(link);
 }
 
 static struct pcmcia_device_id pcmciamtd_ids[] = {
@@ -794,7 +775,7 @@ static struct pcmcia_driver pcmciamtd_driver = {
        .drv            = {
                .name   = "pcmciamtd"
        },
-       .probe          = pcmciamtd_attach,
+       .probe          = pcmciamtd_probe,
        .remove         = pcmciamtd_detach,
        .owner          = THIS_MODULE,
        .id_table       = pcmciamtd_ids,
index ce99845..066e22b 100644 (file)
@@ -539,8 +539,7 @@ rx_status_loop:
                unsigned buflen;
 
                skb = cp->rx_skb[rx_tail].skb;
-               if (!skb)
-                       BUG();
+               BUG_ON(!skb);
 
                desc = &cp->rx_ring[rx_tail];
                status = le32_to_cpu(desc->opts1);
@@ -723,8 +722,7 @@ static void cp_tx (struct cp_private *cp)
                        break;
 
                skb = cp->tx_skb[tx_tail].skb;
-               if (!skb)
-                       BUG();
+               BUG_ON(!skb);
 
                pci_unmap_single(cp->pdev, cp->tx_skb[tx_tail].mapping,
                                 cp->tx_skb[tx_tail].len, PCI_DMA_TODEVICE);
@@ -1550,8 +1548,7 @@ static void cp_get_ethtool_stats (struct net_device *dev,
        tmp_stats[i++] = le16_to_cpu(nic_stats->tx_abort);
        tmp_stats[i++] = le16_to_cpu(nic_stats->tx_underrun);
        tmp_stats[i++] = cp->cp_stats.rx_frags;
-       if (i != CP_NUM_STATS)
-               BUG();
+       BUG_ON(i != CP_NUM_STATS);
 
        pci_free_consistent(cp->pdev, sizeof(*nic_stats), nic_stats, dma);
 }
@@ -1856,8 +1853,7 @@ static void cp_remove_one (struct pci_dev *pdev)
        struct net_device *dev = pci_get_drvdata(pdev);
        struct cp_private *cp = netdev_priv(dev);
 
-       if (!dev)
-               BUG();
+       BUG_ON(!dev);
        unregister_netdev(dev);
        iounmap(cp->regs);
        if (cp->wol_enabled) pci_set_power_state (pdev, PCI_D0);
index 64e2caf..fabc060 100644 (file)
@@ -765,8 +765,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        BUGMSG(D_DURING, "in arcnet_interrupt\n");
        
        lp = dev->priv;
-       if (!lp)
-               BUG();
+       BUG_ON(!lp);
                
        spin_lock(&lp->lock);
 
index 43150b2..0d45553 100644 (file)
@@ -125,11 +125,11 @@ static void __init com90xx_probe(void)
        if (!io && !irq && !shmem && !*device && com90xx_skip_probe)
                return;
 
-       shmems = kzalloc(((0x10000-0xa0000) / 0x800) * sizeof(unsigned long),
+       shmems = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(unsigned long),
                         GFP_KERNEL);
        if (!shmems)
                return;
-       iomem = kzalloc(((0x10000-0xa0000) / 0x800) * sizeof(void __iomem *),
+       iomem = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(void __iomem *),
                         GFP_KERNEL);
        if (!iomem) {
                kfree(shmems);
index 15032f2..c4e12b5 100644 (file)
@@ -608,8 +608,7 @@ static void b44_tx(struct b44 *bp)
                struct ring_info *rp = &bp->tx_buffers[cons];
                struct sk_buff *skb = rp->skb;
 
-               if (unlikely(skb == NULL))
-                       BUG();
+               BUG_ON(skb == NULL);
 
                pci_unmap_single(bp->pdev,
                                 pci_unmap_addr(rp, mapping),
index 30ff8ea..4391bf4 100644 (file)
@@ -1093,8 +1093,7 @@ static int process_responses(struct adapter *adapter, int budget)
                if (likely(e->DataValid)) {
                        struct freelQ *fl = &sge->freelQ[e->FreelistQid];
 
-                       if (unlikely(!e->Sop || !e->Eop))
-                               BUG();
+                       BUG_ON(!e->Sop || !e->Eop);
                        if (unlikely(e->Offload))
                                unexpected_offload(adapter, fl);
                        else
index 49cd096..add8dc4 100644 (file)
@@ -3308,8 +3308,7 @@ e1000_clean(struct net_device *poll_dev, int *budget)
 
        while (poll_dev != &adapter->polling_netdev[i]) {
                i++;
-               if (unlikely(i == adapter->num_rx_queues))
-                       BUG();
+               BUG_ON(i == adapter->num_rx_queues);
        }
 
        if (likely(adapter->num_tx_queues == 1)) {
index aa15691..815436c 100644 (file)
@@ -203,8 +203,7 @@ static int eql_open(struct net_device *dev)
        printk(KERN_INFO "%s: remember to turn off Van-Jacobson compression on "
               "your slave devices.\n", dev->name);
 
-       if (!list_empty(&eql->queue.all_slaves))
-               BUG();
+       BUG_ON(!list_empty(&eql->queue.all_slaves));
 
        eql->min_slaves = 1;
        eql->max_slaves = EQL_DEFAULT_MAX_SLAVES; /* 4 usually... */
index ceb98fd..52d0102 100644 (file)
@@ -235,7 +235,7 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc
 
                lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc);
                    
-               if(lpar_rc != H_Success) {
+               if(lpar_rc != H_SUCCESS) {
                        pool->free_map[free_index] = index;
                        pool->skbuff[index] = NULL;
                        pool->consumer_index--;
@@ -373,7 +373,7 @@ static void ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter)
 
        lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc);
                    
-       if(lpar_rc != H_Success) {
+       if(lpar_rc != H_SUCCESS) {
                ibmveth_debug_printk("h_add_logical_lan_buffer failed during recycle rc=%ld", lpar_rc);
                ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator);
        }
@@ -511,7 +511,7 @@ static int ibmveth_open(struct net_device *netdev)
                                         adapter->filter_list_dma,
                                         mac_address);
 
-       if(lpar_rc != H_Success) {
+       if(lpar_rc != H_SUCCESS) {
                ibmveth_error_printk("h_register_logical_lan failed with %ld\n", lpar_rc);
                ibmveth_error_printk("buffer TCE:0x%lx filter TCE:0x%lx rxq desc:0x%lx MAC:0x%lx\n",
                                     adapter->buffer_list_dma,
@@ -527,7 +527,7 @@ static int ibmveth_open(struct net_device *netdev)
                ibmveth_error_printk("unable to request irq 0x%x, rc %d\n", netdev->irq, rc);
                do {
                        rc = h_free_logical_lan(adapter->vdev->unit_address);
-               } while (H_isLongBusy(rc) || (rc == H_Busy));
+               } while (H_IS_LONG_BUSY(rc) || (rc == H_BUSY));
 
                ibmveth_cleanup(adapter);
                return rc;
@@ -556,9 +556,9 @@ static int ibmveth_close(struct net_device *netdev)
 
        do {
                lpar_rc = h_free_logical_lan(adapter->vdev->unit_address);
-       } while (H_isLongBusy(lpar_rc) || (lpar_rc == H_Busy));
+       } while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
 
-       if(lpar_rc != H_Success)
+       if(lpar_rc != H_SUCCESS)
        {
                ibmveth_error_printk("h_free_logical_lan failed with %lx, continuing with close\n",
                                     lpar_rc);
@@ -693,9 +693,9 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev)
                                             desc[4].desc,
                                             desc[5].desc,
                                             correlator);
-       } while ((lpar_rc == H_Busy) && (retry_count--));
+       } while ((lpar_rc == H_BUSY) && (retry_count--));
     
-       if(lpar_rc != H_Success && lpar_rc != H_Dropped) {
+       if(lpar_rc != H_SUCCESS && lpar_rc != H_DROPPED) {
                int i;
                ibmveth_error_printk("tx: h_send_logical_lan failed with rc=%ld\n", lpar_rc);
                for(i = 0; i < 6; i++) {
@@ -786,14 +786,14 @@ static int ibmveth_poll(struct net_device *netdev, int *budget)
        /* we think we are done - reenable interrupts, then check once more to make sure we are done */
        lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_ENABLE);
 
-       ibmveth_assert(lpar_rc == H_Success);
+       ibmveth_assert(lpar_rc == H_SUCCESS);
 
        netif_rx_complete(netdev);
 
        if(ibmveth_rxq_pending_buffer(adapter) && netif_rx_reschedule(netdev, frames_processed))
        {
                lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE);
-               ibmveth_assert(lpar_rc == H_Success);
+               ibmveth_assert(lpar_rc == H_SUCCESS);
                more_work = 1;
                goto restart_poll;
        }
@@ -813,7 +813,7 @@ static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance, struct pt_regs
 
        if(netif_rx_schedule_prep(netdev)) {
                lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE);
-               ibmveth_assert(lpar_rc == H_Success);
+               ibmveth_assert(lpar_rc == H_SUCCESS);
                __netif_rx_schedule(netdev);
        }
        return IRQ_HANDLED;
@@ -835,7 +835,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev)
                                           IbmVethMcastEnableRecv |
                                           IbmVethMcastDisableFiltering,
                                           0);
-               if(lpar_rc != H_Success) {
+               if(lpar_rc != H_SUCCESS) {
                        ibmveth_error_printk("h_multicast_ctrl rc=%ld when entering promisc mode\n", lpar_rc);
                }
        } else {
@@ -847,7 +847,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev)
                                           IbmVethMcastDisableFiltering |
                                           IbmVethMcastClearFilterTable,
                                           0);
-               if(lpar_rc != H_Success) {
+               if(lpar_rc != H_SUCCESS) {
                        ibmveth_error_printk("h_multicast_ctrl rc=%ld when attempting to clear filter table\n", lpar_rc);
                }
                /* add the addresses to the filter table */
@@ -858,7 +858,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev)
                        lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
                                                   IbmVethMcastAddFilter,
                                                   mcast_addr);
-                       if(lpar_rc != H_Success) {
+                       if(lpar_rc != H_SUCCESS) {
                                ibmveth_error_printk("h_multicast_ctrl rc=%ld when adding an entry to the filter table\n", lpar_rc);
                        }
                }
@@ -867,7 +867,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev)
                lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
                                           IbmVethMcastEnableFiltering,
                                           0);
-               if(lpar_rc != H_Success) {
+               if(lpar_rc != H_SUCCESS) {
                        ibmveth_error_printk("h_multicast_ctrl rc=%ld when enabling filtering\n", lpar_rc);
                }
        }
index 63d38fb..f530686 100644 (file)
@@ -695,8 +695,7 @@ static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
                /*
                 * We must not be transmitting...
                 */
-               if (si->txskb)
-                       BUG();
+               BUG_ON(si->txskb);
 
                netif_stop_queue(dev);
 
index d11821d..ced9fdb 100644 (file)
@@ -645,9 +645,7 @@ static void __devexit ne2k_pci_remove_one (struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
 
-       if (!dev)
-               BUG();
-
+       BUG_ON(!dev);
        unregister_netdev(dev);
        release_region(dev->base_addr, NE_IO_EXTENT);
        free_netdev(dev);
index 8e9b1a5..706aed7 100644 (file)
@@ -568,8 +568,7 @@ static inline int ns83820_add_rx_skb(struct ns83820 *dev, struct sk_buff *skb)
 #endif
 
        sg = dev->rx_info.descs + (next_empty * DESC_SIZE);
-       if (unlikely(NULL != dev->rx_info.skbs[next_empty]))
-               BUG();
+       BUG_ON(NULL != dev->rx_info.skbs[next_empty]);
        dev->rx_info.skbs[next_empty] = skb;
 
        dev->rx_info.next_empty = (next_empty + 1) % NR_RX_DESC;
index ce90bec..fab9336 100644 (file)
@@ -204,7 +204,7 @@ enum Window4 {              /* Window 4: Xcvr/media bits. */
 #define MEDIA_TP       0x00C0  /* Enable link beat and jabber for 10baseT. */
 
 struct el3_private {
-       dev_link_t link;
+       struct pcmcia_device    *p_dev;
        dev_node_t node;
        struct net_device_stats stats;
        u16 advertising, partner;               /* NWay media advertisement */
@@ -225,8 +225,8 @@ static char mii_preamble_required = 0;
 
 /* Index of functions. */
 
-static void tc574_config(dev_link_t *link);
-static void tc574_release(dev_link_t *link);
+static int tc574_config(struct pcmcia_device *link);
+static void tc574_release(struct pcmcia_device *link);
 
 static void mdio_sync(kio_addr_t ioaddr, int bits);
 static int mdio_read(kio_addr_t ioaddr, int phy_id, int location);
@@ -256,10 +256,9 @@ static void tc574_detach(struct pcmcia_device *p_dev);
        with Card Services.
 */
 
-static int tc574_attach(struct pcmcia_device *p_dev)
+static int tc574_probe(struct pcmcia_device *link)
 {
        struct el3_private *lp;
-       dev_link_t *link;
        struct net_device *dev;
 
        DEBUG(0, "3c574_attach()\n");
@@ -269,8 +268,8 @@ static int tc574_attach(struct pcmcia_device *p_dev)
        if (!dev)
                return -ENOMEM;
        lp = netdev_priv(dev);
-       link = &lp->link;
        link->priv = dev;
+       lp->p_dev = link;
 
        spin_lock_init(&lp->window_lock);
        link->io.NumPorts1 = 32;
@@ -280,7 +279,6 @@ static int tc574_attach(struct pcmcia_device *p_dev)
        link->irq.Handler = &el3_interrupt;
        link->irq.Instance = dev;
        link->conf.Attributes = CONF_ENABLE_IRQ;
-       link->conf.Vcc = 50;
        link->conf.IntType = INT_MEMORY_AND_IO;
        link->conf.ConfigIndex = 1;
        link->conf.Present = PRESENT_OPTION;
@@ -298,13 +296,7 @@ static int tc574_attach(struct pcmcia_device *p_dev)
        dev->watchdog_timeo = TX_TIMEOUT;
 #endif
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       tc574_config(link);
-
-       return 0;
+       return tc574_config(link);
 } /* tc574_attach */
 
 /*
@@ -316,18 +308,16 @@ static int tc574_attach(struct pcmcia_device *p_dev)
 
 */
 
-static void tc574_detach(struct pcmcia_device *p_dev)
+static void tc574_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
        DEBUG(0, "3c574_detach(0x%p)\n", link);
 
-       if (link->dev)
+       if (link->dev_node)
                unregister_netdev(dev);
 
-       if (link->state & DEV_CONFIG)
-               tc574_release(link);
+       tc574_release(link);
 
        free_netdev(dev);
 } /* tc574_detach */
@@ -343,9 +333,8 @@ static void tc574_detach(struct pcmcia_device *p_dev)
 
 static const char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
 
-static void tc574_config(dev_link_t *link)
+static int tc574_config(struct pcmcia_device *link)
 {
-       client_handle_t handle = link->handle;
        struct net_device *dev = link->priv;
        struct el3_private *lp = netdev_priv(dev);
        tuple_t tuple;
@@ -363,30 +352,27 @@ static void tc574_config(dev_link_t *link)
 
        tuple.Attributes = 0;
        tuple.DesiredTuple = CISTPL_CONFIG;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        tuple.TupleData = (cisdata_t *)buf;
        tuple.TupleDataMax = 64;
        tuple.TupleOffset = 0;
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
        link->io.IOAddrLines = 16;
        for (i = j = 0; j < 0x400; j += 0x20) {
                link->io.BasePort1 = j ^ 0x300;
-               i = pcmcia_request_io(link->handle, &link->io);
+               i = pcmcia_request_io(link, &link->io);
                if (i == CS_SUCCESS) break;
        }
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestIO, i);
+               cs_error(link, RequestIO, i);
                goto failed;
        }
-       CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+       CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
        dev->irq = link->irq.AssignedIRQ;
        dev->base_addr = link->io.BasePort1;
@@ -397,8 +383,8 @@ static void tc574_config(dev_link_t *link)
           the hardware address.  The future products may include a modem chip
           and put the address in the CIS. */
        tuple.DesiredTuple = 0x88;
-       if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) {
-               pcmcia_get_tuple_data(handle, &tuple);
+       if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) {
+               pcmcia_get_tuple_data(link, &tuple);
                for (i = 0; i < 3; i++)
                        phys_addr[i] = htons(buf[i]);
        } else {
@@ -412,9 +398,9 @@ static void tc574_config(dev_link_t *link)
                }
        }
        tuple.DesiredTuple = CISTPL_VERS_1;
-       if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS &&
-               pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS &&
-               pcmcia_parse_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
+       if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS &&
+               pcmcia_get_tuple_data(link, &tuple) == CS_SUCCESS &&
+               pcmcia_parse_tuple(link, &tuple, &parse) == CS_SUCCESS) {
                cardname = parse.version_1.str + parse.version_1.ofs[1];
        } else
                cardname = "3Com 3c574";
@@ -473,13 +459,12 @@ static void tc574_config(dev_link_t *link)
                }
        }
 
-       link->state &= ~DEV_CONFIG_PENDING;
-       link->dev = &lp->node;
-       SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+       link->dev_node = &lp->node;
+       SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
        if (register_netdev(dev) != 0) {
                printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n");
-               link->dev = NULL;
+               link->dev_node = NULL;
                goto failed;
        }
 
@@ -493,13 +478,13 @@ static void tc574_config(dev_link_t *link)
                   8 << config.u.ram_size, ram_split[config.u.ram_split],
                   config.u.autoselect ? "autoselect " : "");
 
-       return;
+       return 0;
 
 cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 failed:
        tc574_release(link);
-       return;
+       return -ENODEV;
 
 } /* tc574_config */
 
@@ -509,44 +494,28 @@ failed:
        still open, this will be postponed until it is closed.
 */
 
-static void tc574_release(dev_link_t *link)
+static void tc574_release(struct pcmcia_device *link)
 {
-       DEBUG(0, "3c574_release(0x%p)\n", link);
-
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
-
-       link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
 }
 
-static int tc574_suspend(struct pcmcia_device *p_dev)
+static int tc574_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-       }
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int tc574_resume(struct pcmcia_device *p_dev)
+static int tc574_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       tc574_reset(dev);
-                       netif_device_attach(dev);
-               }
+       if (link->open) {
+               tc574_reset(dev);
+               netif_device_attach(dev);
        }
 
        return 0;
@@ -757,9 +726,9 @@ static void tc574_reset(struct net_device *dev)
 static int el3_open(struct net_device *dev)
 {
        struct el3_private *lp = netdev_priv(dev);
-       dev_link_t *link = &lp->link;
+       struct pcmcia_device *link = lp->p_dev;
 
-       if (!DEV_OK(link))
+       if (!pcmcia_dev_present(link))
                return -ENODEV;
        
        link->open++;
@@ -1203,11 +1172,11 @@ static int el3_close(struct net_device *dev)
 {
        kio_addr_t ioaddr = dev->base_addr;
        struct el3_private *lp = netdev_priv(dev);
-       dev_link_t *link = &lp->link;
+       struct pcmcia_device *link = lp->p_dev;
 
        DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
        
-       if (DEV_OK(link)) {
+       if (pcmcia_dev_present(link)) {
                unsigned long flags;
 
                /* Turn off statistics ASAP.  We update lp->stats below. */
@@ -1246,7 +1215,7 @@ static struct pcmcia_driver tc574_driver = {
        .drv            = {
                .name   = "3c574_cs",
        },
-       .probe          = tc574_attach,
+       .probe          = tc574_probe,
        .remove         = tc574_detach,
        .id_table       = tc574_ids,
        .suspend        = tc574_suspend,
index 3dba508..875a0fe 100644 (file)
@@ -105,7 +105,7 @@ enum RxFilter {
 #define TX_TIMEOUT     ((400*HZ)/1000)
 
 struct el3_private {
-    dev_link_t         link;
+       struct pcmcia_device    *p_dev;
     dev_node_t                 node;
     struct net_device_stats stats;
     /* For transceiver monitoring */
@@ -142,8 +142,8 @@ DRV_NAME ".c " DRV_VERSION " 2001/10/13 00:08:50 (David Hinds)";
 
 /*====================================================================*/
 
-static void tc589_config(dev_link_t *link);
-static void tc589_release(dev_link_t *link);
+static int tc589_config(struct pcmcia_device *link);
+static void tc589_release(struct pcmcia_device *link);
 
 static u16 read_eeprom(kio_addr_t ioaddr, int index);
 static void tc589_reset(struct net_device *dev);
@@ -170,10 +170,9 @@ static void tc589_detach(struct pcmcia_device *p_dev);
 
 ======================================================================*/
 
-static int tc589_attach(struct pcmcia_device *p_dev)
+static int tc589_probe(struct pcmcia_device *link)
 {
     struct el3_private *lp;
-    dev_link_t *link;
     struct net_device *dev;
 
     DEBUG(0, "3c589_attach()\n");
@@ -183,8 +182,8 @@ static int tc589_attach(struct pcmcia_device *p_dev)
     if (!dev)
         return -ENOMEM;
     lp = netdev_priv(dev);
-    link = &lp->link;
     link->priv = dev;
+    lp->p_dev = link;
 
     spin_lock_init(&lp->lock);
     link->io.NumPorts1 = 16;
@@ -194,7 +193,6 @@ static int tc589_attach(struct pcmcia_device *p_dev)
     link->irq.Handler = &el3_interrupt;
     link->irq.Instance = dev;
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
     link->conf.ConfigIndex = 1;
     link->conf.Present = PRESENT_OPTION;
@@ -213,13 +211,7 @@ static int tc589_attach(struct pcmcia_device *p_dev)
 #endif
     SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    tc589_config(link);
-
-    return 0;
+    return tc589_config(link);
 } /* tc589_attach */
 
 /*======================================================================
@@ -231,18 +223,16 @@ static int tc589_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void tc589_detach(struct pcmcia_device *p_dev)
+static void tc589_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
     struct net_device *dev = link->priv;
 
     DEBUG(0, "3c589_detach(0x%p)\n", link);
 
-    if (link->dev)
+    if (link->dev_node)
        unregister_netdev(dev);
 
-    if (link->state & DEV_CONFIG)
-       tc589_release(link);
+    tc589_release(link);
 
     free_netdev(dev);
 } /* tc589_detach */
@@ -258,9 +248,8 @@ static void tc589_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void tc589_config(dev_link_t *link)
+static int tc589_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     struct net_device *dev = link->priv;
     struct el3_private *lp = netdev_priv(dev);
     tuple_t tuple;
@@ -275,43 +264,40 @@ static void tc589_config(dev_link_t *link)
     phys_addr = (u16 *)dev->dev_addr;
     tuple.Attributes = 0;
     tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     tuple.TupleData = (cisdata_t *)buf;
     tuple.TupleDataMax = sizeof(buf);
     tuple.TupleOffset = 0;
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present = parse.config.rmask[0];
     
     /* Is this a 3c562? */
     tuple.DesiredTuple = CISTPL_MANFID;
     tuple.Attributes = TUPLE_RETURN_COMMON;
-    if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
-       (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) {
+    if ((pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) &&
+       (pcmcia_get_tuple_data(link, &tuple) == CS_SUCCESS)) {
        if (le16_to_cpu(buf[0]) != MANFID_3COM)
            printk(KERN_INFO "3c589_cs: hmmm, is this really a "
                   "3Com card??\n");
        multi = (le16_to_cpu(buf[1]) == PRODID_3COM_3C562);
     }
-    
-    /* Configure card */
-    link->state |= DEV_CONFIG;
 
     /* For the 3c562, the base address must be xx00-xx7f */
     link->io.IOAddrLines = 16;
     for (i = j = 0; j < 0x400; j += 0x10) {
        if (multi && (j & 0x80)) continue;
        link->io.BasePort1 = j ^ 0x300;
-       i = pcmcia_request_io(link->handle, &link->io);
+       i = pcmcia_request_io(link, &link->io);
        if (i == CS_SUCCESS) break;
     }
     if (i != CS_SUCCESS) {
-       cs_error(link->handle, RequestIO, i);
+       cs_error(link, RequestIO, i);
        goto failed;
     }
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
        
     dev->irq = link->irq.AssignedIRQ;
     dev->base_addr = link->io.BasePort1;
@@ -321,8 +307,8 @@ static void tc589_config(dev_link_t *link)
     /* The 3c589 has an extra EEPROM for configuration info, including
        the hardware address.  The 3c562 puts the address in the CIS. */
     tuple.DesiredTuple = 0x88;
-    if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) {
-       pcmcia_get_tuple_data(handle, &tuple);
+    if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) {
+       pcmcia_get_tuple_data(link, &tuple);
        for (i = 0; i < 3; i++)
            phys_addr[i] = htons(buf[i]);
     } else {
@@ -346,13 +332,12 @@ static void tc589_config(dev_link_t *link)
     else
        printk(KERN_ERR "3c589_cs: invalid if_port requested\n");
     
-    link->dev = &lp->node;
-    link->state &= ~DEV_CONFIG_PENDING;
-    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+    link->dev_node = &lp->node;
+    SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
     if (register_netdev(dev) != 0) {
        printk(KERN_ERR "3c589_cs: register_netdev() failed\n");
-       link->dev = NULL;
+       link->dev_node = NULL;
        goto failed;
     }
 
@@ -366,14 +351,13 @@ static void tc589_config(dev_link_t *link)
     printk(KERN_INFO "  %dK FIFO split %s Rx:Tx, %s xcvr\n",
           (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
           if_names[dev->if_port]);
-    return;
+    return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 failed:
     tc589_release(link);
-    return;
-    
+    return -ENODEV;
 } /* tc589_config */
 
 /*======================================================================
@@ -384,44 +368,28 @@ failed:
     
 ======================================================================*/
 
-static void tc589_release(dev_link_t *link)
+static void tc589_release(struct pcmcia_device *link)
 {
-    DEBUG(0, "3c589_release(0x%p)\n", link);
-    
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    
-    link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
 }
 
-static int tc589_suspend(struct pcmcia_device *p_dev)
+static int tc589_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-       }
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int tc589_resume(struct pcmcia_device *p_dev)
+static int tc589_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       tc589_reset(dev);
-                       netif_device_attach(dev);
-               }
+       if (link->open) {
+               tc589_reset(dev);
+               netif_device_attach(dev);
        }
 
        return 0;
@@ -587,9 +555,9 @@ static int el3_config(struct net_device *dev, struct ifmap *map)
 static int el3_open(struct net_device *dev)
 {
     struct el3_private *lp = netdev_priv(dev);
-    dev_link_t *link = &lp->link;
+    struct pcmcia_device *link = lp->p_dev;
     
-    if (!DEV_OK(link))
+    if (!pcmcia_dev_present(link))
        return -ENODEV;
 
     link->open++;
@@ -848,9 +816,9 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev)
 {
     struct el3_private *lp = netdev_priv(dev);
     unsigned long flags;
-    dev_link_t *link = &lp->link;
+    struct pcmcia_device *link = lp->p_dev;
 
-    if (DEV_OK(link)) {
+    if (pcmcia_dev_present(link)) {
        spin_lock_irqsave(&lp->lock, flags);
        update_stats(dev);
        spin_unlock_irqrestore(&lp->lock, flags);
@@ -950,11 +918,11 @@ static int el3_rx(struct net_device *dev)
 static void set_multicast_list(struct net_device *dev)
 {
     struct el3_private *lp = netdev_priv(dev);
-    dev_link_t *link = &lp->link;
+    struct pcmcia_device *link = lp->p_dev;
     kio_addr_t ioaddr = dev->base_addr;
     u16 opts = SetRxFilter | RxStation | RxBroadcast;
 
-    if (!(DEV_OK(link))) return;
+    if (!pcmcia_dev_present(link)) return;
     if (dev->flags & IFF_PROMISC)
        opts |= RxMulticast | RxProm;
     else if (dev->mc_count || (dev->flags & IFF_ALLMULTI))
@@ -965,12 +933,12 @@ static void set_multicast_list(struct net_device *dev)
 static int el3_close(struct net_device *dev)
 {
     struct el3_private *lp = netdev_priv(dev);
-    dev_link_t *link = &lp->link;
+    struct pcmcia_device *link = lp->p_dev;
     kio_addr_t ioaddr = dev->base_addr;
     
     DEBUG(1, "%s: shutting down ethercard.\n", dev->name);
 
-    if (DEV_OK(link)) {
+    if (pcmcia_dev_present(link)) {
        /* Turn off statistics ASAP.  We update lp->stats below. */
        outw(StatsDisable, ioaddr + EL3_CMD);
        
@@ -1020,7 +988,7 @@ static struct pcmcia_driver tc589_driver = {
        .drv            = {
                .name   = "3c589_cs",
        },
-       .probe          = tc589_attach,
+       .probe          = tc589_probe,
        .remove         = tc589_detach,
         .id_table       = tc589_ids,
        .suspend        = tc589_suspend,
index 1cc94b2..56233af 100644 (file)
@@ -86,8 +86,8 @@ static char *version =
 
 /*====================================================================*/
 
-static void axnet_config(dev_link_t *link);
-static void axnet_release(dev_link_t *link);
+static int axnet_config(struct pcmcia_device *link);
+static void axnet_release(struct pcmcia_device *link);
 static int axnet_open(struct net_device *dev);
 static int axnet_close(struct net_device *dev);
 static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -117,7 +117,7 @@ static irqreturn_t ax_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 /*====================================================================*/
 
 typedef struct axnet_dev_t {
-    dev_link_t         link;
+       struct pcmcia_device    *p_dev;
     dev_node_t         node;
     caddr_t            base;
     struct timer_list  watchdog;
@@ -142,10 +142,9 @@ static inline axnet_dev_t *PRIV(struct net_device *dev)
 
 ======================================================================*/
 
-static int axnet_attach(struct pcmcia_device *p_dev)
+static int axnet_probe(struct pcmcia_device *link)
 {
     axnet_dev_t *info;
-    dev_link_t *link;
     struct net_device *dev;
 
     DEBUG(0, "axnet_attach()\n");
@@ -157,7 +156,7 @@ static int axnet_attach(struct pcmcia_device *p_dev)
        return -ENOMEM;
 
     info = PRIV(dev);
-    link = &info->link;
+    info->p_dev = link;
     link->priv = dev;
     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
     link->irq.IRQInfo1 = IRQ_LEVEL_ID;
@@ -169,13 +168,7 @@ static int axnet_attach(struct pcmcia_device *p_dev)
     dev->do_ioctl = &axnet_ioctl;
     SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    axnet_config(link);
-
-    return 0;
+    return axnet_config(link);
 } /* axnet_attach */
 
 /*======================================================================
@@ -187,18 +180,16 @@ static int axnet_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void axnet_detach(struct pcmcia_device *p_dev)
+static void axnet_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
     struct net_device *dev = link->priv;
 
     DEBUG(0, "axnet_detach(0x%p)\n", link);
 
-    if (link->dev)
+    if (link->dev_node)
        unregister_netdev(dev);
 
-    if (link->state & DEV_CONFIG)
-       axnet_release(link);
+    axnet_release(link);
 
     free_netdev(dev);
 } /* axnet_detach */
@@ -209,7 +200,7 @@ static void axnet_detach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static int get_prom(dev_link_t *link)
+static int get_prom(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     kio_addr_t ioaddr = dev->base_addr;
@@ -263,7 +254,7 @@ static int get_prom(dev_link_t *link)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static int try_io_port(dev_link_t *link)
+static int try_io_port(struct pcmcia_device *link)
 {
     int j, ret;
     if (link->io.NumPorts1 == 32) {
@@ -284,25 +275,23 @@ static int try_io_port(dev_link_t *link)
        for (j = 0; j < 0x400; j += 0x20) {
            link->io.BasePort1 = j ^ 0x300;
            link->io.BasePort2 = (j ^ 0x300) + 0x10;
-           ret = pcmcia_request_io(link->handle, &link->io);
+           ret = pcmcia_request_io(link, &link->io);
            if (ret == CS_SUCCESS) return ret;
        }
        return ret;
     } else {
-       return pcmcia_request_io(link->handle, &link->io);
+       return pcmcia_request_io(link, &link->io);
     }
 }
 
-static void axnet_config(dev_link_t *link)
+static int axnet_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     struct net_device *dev = link->priv;
     axnet_dev_t *info = PRIV(dev);
     tuple_t tuple;
     cisparse_t parse;
     int i, j, last_ret, last_fn;
     u_short buf[64];
-    config_info_t conf;
 
     DEBUG(0, "axnet_config(0x%p)\n", link);
 
@@ -311,29 +300,22 @@ static void axnet_config(dev_link_t *link)
     tuple.TupleDataMax = sizeof(buf);
     tuple.TupleOffset = 0;
     tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
     /* don't trust the CIS on this; Linksys got it wrong */
     link->conf.Present = 0x63;
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
-    /* Look up current Vcc */
-    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
-    link->conf.Vcc = conf.Vcc;
-
     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
     tuple.Attributes = 0;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     while (last_ret == CS_SUCCESS) {
        cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
        cistpl_io_t *io = &(parse.cftable_entry.io);
        
-       if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-               pcmcia_parse_tuple(handle, &tuple, &parse) != 0 ||
+       if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+               pcmcia_parse_tuple(link, &tuple, &parse) != 0 ||
                cfg->index == 0 || cfg->io.nwin == 0)
            goto next_entry;
        
@@ -355,21 +337,21 @@ static void axnet_config(dev_link_t *link)
            if (last_ret == CS_SUCCESS) break;
        }
     next_entry:
-       last_ret = pcmcia_get_next_tuple(handle, &tuple);
+       last_ret = pcmcia_get_next_tuple(link, &tuple);
     }
     if (last_ret != CS_SUCCESS) {
-       cs_error(handle, RequestIO, last_ret);
+       cs_error(link, RequestIO, last_ret);
        goto failed;
     }
 
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
     
     if (link->io.NumPorts2 == 8) {
        link->conf.Attributes |= CONF_ENABLE_SPKR;
        link->conf.Status = CCSR_AUDIO_ENA;
     }
     
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
     dev->irq = link->irq.AssignedIRQ;
     dev->base_addr = link->io.BasePort1;
 
@@ -406,7 +388,7 @@ static void axnet_config(dev_link_t *link)
        Bit 2 of CCSR is active low. */ 
     if (i == 32) {
        conf_reg_t reg = { 0, CS_WRITE, CISREG_CCSR, 0x04 };
-       pcmcia_access_configuration_register(link->handle, &reg);
+       pcmcia_access_configuration_register(link, &reg);
        for (i = 0; i < 32; i++) {
            j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
            if ((j != 0) && (j != 0xffff)) break;
@@ -414,13 +396,12 @@ static void axnet_config(dev_link_t *link)
     }
 
     info->phy_id = (i < 32) ? i : -1;
-    link->dev = &info->node;
-    link->state &= ~DEV_CONFIG_PENDING;
-    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+    link->dev_node = &info->node;
+    SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
     if (register_netdev(dev) != 0) {
        printk(KERN_NOTICE "axnet_cs: register_netdev() failed\n");
-       link->dev = NULL;
+       link->dev_node = NULL;
        goto failed;
     }
 
@@ -436,14 +417,13 @@ static void axnet_config(dev_link_t *link)
     } else {
        printk(KERN_NOTICE "  No MII transceivers found!\n");
     }
-    return;
+    return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 failed:
     axnet_release(link);
-    link->state &= ~DEV_CONFIG_PENDING;
-    return;
+    return -ENODEV;
 } /* axnet_config */
 
 /*======================================================================
@@ -454,45 +434,29 @@ failed:
 
 ======================================================================*/
 
-static void axnet_release(dev_link_t *link)
+static void axnet_release(struct pcmcia_device *link)
 {
-    DEBUG(0, "axnet_release(0x%p)\n", link);
-
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-
-    link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
 }
 
-static int axnet_suspend(struct pcmcia_device *p_dev)
+static int axnet_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-       }
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int axnet_resume(struct pcmcia_device *p_dev)
+static int axnet_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       axnet_reset_8390(dev);
-                       AX88190_init(dev, 1);
-                       netif_device_attach(dev);
-               }
+       if (link->open) {
+               axnet_reset_8390(dev);
+               AX88190_init(dev, 1);
+               netif_device_attach(dev);
        }
 
        return 0;
@@ -562,11 +526,11 @@ static void mdio_write(kio_addr_t addr, int phy_id, int loc, int value)
 static int axnet_open(struct net_device *dev)
 {
     axnet_dev_t *info = PRIV(dev);
-    dev_link_t *link = &info->link;
+    struct pcmcia_device *link = info->p_dev;
     
     DEBUG(2, "axnet_open('%s')\n", dev->name);
 
-    if (!DEV_OK(link))
+    if (!pcmcia_dev_present(link))
        return -ENODEV;
 
     link->open++;
@@ -588,7 +552,7 @@ static int axnet_open(struct net_device *dev)
 static int axnet_close(struct net_device *dev)
 {
     axnet_dev_t *info = PRIV(dev);
-    dev_link_t *link = &info->link;
+    struct pcmcia_device *link = info->p_dev;
 
     DEBUG(2, "axnet_close('%s')\n", dev->name);
 
@@ -833,7 +797,7 @@ static struct pcmcia_driver axnet_cs_driver = {
        .drv            = {
                .name   = "axnet_cs",
        },
-       .probe          = axnet_attach,
+       .probe          = axnet_probe,
        .remove         = axnet_detach,
        .id_table       = axnet_ids,
        .suspend        = axnet_suspend,
index 2827a48..441de82 100644 (file)
@@ -118,8 +118,8 @@ MODULE_LICENSE("GPL");
 
 /*====================================================================*/
 
-static void com20020_config(dev_link_t *link);
-static void com20020_release(dev_link_t *link);
+static int com20020_config(struct pcmcia_device *link);
+static void com20020_release(struct pcmcia_device *link);
 
 static void com20020_detach(struct pcmcia_device *p_dev);
 
@@ -138,9 +138,8 @@ typedef struct com20020_dev_t {
 
 ======================================================================*/
 
-static int com20020_attach(struct pcmcia_device *p_dev)
+static int com20020_probe(struct pcmcia_device *p_dev)
 {
-    dev_link_t *link;
     com20020_dev_t *info;
     struct net_device *dev;
     struct arcnet_local *lp;
@@ -148,10 +147,6 @@ static int com20020_attach(struct pcmcia_device *p_dev)
     DEBUG(0, "com20020_attach()\n");
 
     /* Create new network device */
-    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
-    if (!link)
-       return -ENOMEM;
-
     info = kmalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
     if (!info)
        goto fail_alloc_info;
@@ -161,7 +156,6 @@ static int com20020_attach(struct pcmcia_device *p_dev)
        goto fail_alloc_dev;
 
     memset(info, 0, sizeof(struct com20020_dev_t));
-    memset(link, 0, sizeof(struct dev_link_t));
     lp = dev->priv;
     lp->timeout = timeout;
     lp->backplane = backplane;
@@ -172,28 +166,23 @@ static int com20020_attach(struct pcmcia_device *p_dev)
     /* fill in our module parameters as defaults */
     dev->dev_addr[0] = node;
 
-    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-    link->io.NumPorts1 = 16;
-    link->io.IOAddrLines = 16;
-    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
-    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
-    link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
-    link->conf.IntType = INT_MEMORY_AND_IO;
-    link->conf.Present = PRESENT_OPTION;
-
-    link->irq.Instance = info->dev = dev;
-    link->priv = info;
+    p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    p_dev->io.NumPorts1 = 16;
+    p_dev->io.IOAddrLines = 16;
+    p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    p_dev->conf.Attributes = CONF_ENABLE_IRQ;
+    p_dev->conf.IntType = INT_MEMORY_AND_IO;
+    p_dev->conf.Present = PRESENT_OPTION;
 
-    link->state |= DEV_PRESENT;
-    com20020_config(link);
+    p_dev->irq.Instance = info->dev = dev;
+    p_dev->priv = info;
 
-    return 0;
+    return com20020_config(p_dev);
 
 fail_alloc_dev:
     kfree(info);
 fail_alloc_info:
-    kfree(link);
     return -ENOMEM;
 } /* com20020_attach */
 
@@ -206,9 +195,8 @@ fail_alloc_info:
 
 ======================================================================*/
 
-static void com20020_detach(struct pcmcia_device *p_dev)
+static void com20020_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
     struct com20020_dev_t *info = link->priv;
     struct net_device *dev = info->dev;
 
@@ -216,7 +204,7 @@ static void com20020_detach(struct pcmcia_device *p_dev)
 
     DEBUG(0, "com20020_detach(0x%p)\n", link);
 
-    if (link->dev) {
+    if (link->dev_node) {
        DEBUG(1,"unregister...\n");
 
        unregister_netdev(dev);
@@ -229,8 +217,7 @@ static void com20020_detach(struct pcmcia_device *p_dev)
            free_irq(dev->irq, dev);
     }
 
-    if (link->state & DEV_CONFIG)
-        com20020_release(link);
+    com20020_release(link);
 
     /* Unlink device structure, free bits */
     DEBUG(1,"unlinking...\n");
@@ -245,8 +232,6 @@ static void com20020_detach(struct pcmcia_device *p_dev)
        DEBUG(1,"kfree2...\n");
        kfree(info);
     }
-    DEBUG(1,"kfree3...\n");
-    kfree(link);
 
 } /* com20020_detach */
 
@@ -261,10 +246,9 @@ static void com20020_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void com20020_config(dev_link_t *link)
+static int com20020_config(struct pcmcia_device *link)
 {
     struct arcnet_local *lp;
-    client_handle_t handle;
     tuple_t tuple;
     cisparse_t parse;
     com20020_dev_t *info;
@@ -273,7 +257,6 @@ static void com20020_config(dev_link_t *link)
     u_char buf[64];
     int ioaddr;
 
-    handle = link->handle;
     info = link->priv;
     dev = info->dev;
 
@@ -286,14 +269,11 @@ static void com20020_config(dev_link_t *link)
     tuple.TupleDataMax = 64;
     tuple.TupleOffset = 0;
     tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
     DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
     i = !CS_SUCCESS;
     if (!link->io.BasePort1)
@@ -301,13 +281,13 @@ static void com20020_config(dev_link_t *link)
        for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
        {
            link->io.BasePort1 = ioaddr;
-           i = pcmcia_request_io(link->handle, &link->io);
+           i = pcmcia_request_io(link, &link->io);
            if (i == CS_SUCCESS)
                break;
        }
     }
     else
-       i = pcmcia_request_io(link->handle, &link->io);
+       i = pcmcia_request_io(link, &link->io);
     
     if (i != CS_SUCCESS)
     {
@@ -321,7 +301,7 @@ static void com20020_config(dev_link_t *link)
     DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
           link->irq.AssignedIRQ,
           link->irq.IRQInfo1, link->irq.IRQInfo2);
-    i = pcmcia_request_irq(link->handle, &link->irq);
+    i = pcmcia_request_irq(link, &link->irq);
     if (i != CS_SUCCESS)
     {
        DEBUG(1,"arcnet: requestIRQ failed totally!\n");
@@ -330,7 +310,7 @@ static void com20020_config(dev_link_t *link)
 
     dev->irq = link->irq.AssignedIRQ;
 
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
     if (com20020_check(dev))
     {
@@ -342,15 +322,14 @@ static void com20020_config(dev_link_t *link)
     lp->card_name = "PCMCIA COM20020";
     lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
 
-    link->dev = &info->node;
-    link->state &= ~DEV_CONFIG_PENDING;
-    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+    link->dev_node = &info->node;
+    SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
     i = com20020_found(dev, 0);        /* calls register_netdev */
     
     if (i != 0) {
        DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
-       link->dev = NULL;
+       link->dev_node = NULL;
        goto failed;
     }
 
@@ -358,13 +337,14 @@ static void com20020_config(dev_link_t *link)
 
     DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
            dev->name, dev->base_addr, dev->irq);
-    return;
+    return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 failed:
     DEBUG(1,"com20020_config failed...\n");
     com20020_release(link);
+    return -ENODEV;
 } /* com20020_config */
 
 /*======================================================================
@@ -375,52 +355,33 @@ failed:
 
 ======================================================================*/
 
-static void com20020_release(dev_link_t *link)
+static void com20020_release(struct pcmcia_device *link)
 {
-
-    DEBUG(1,"release...\n");
-
-    DEBUG(0, "com20020_release(0x%p)\n", link);
-
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-
-    link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+       DEBUG(0, "com20020_release(0x%p)\n", link);
+       pcmcia_disable_device(link);
 }
 
-static int com20020_suspend(struct pcmcia_device *p_dev)
+static int com20020_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        com20020_dev_t *info = link->priv;
        struct net_device *dev = info->dev;
 
-       link->state |= DEV_SUSPEND;
-        if (link->state & DEV_CONFIG) {
-               if (link->open) {
-                       netif_device_detach(dev);
-               }
-               pcmcia_release_configuration(link->handle);
-        }
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int com20020_resume(struct pcmcia_device *p_dev)
+static int com20020_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        com20020_dev_t *info = link->priv;
        struct net_device *dev = info->dev;
 
-       link->state &= ~DEV_SUSPEND;
-        if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       int ioaddr = dev->base_addr;
-                       struct arcnet_local *lp = dev->priv;
-                       ARCRESET;
-               }
-        }
+       if (link->open) {
+               int ioaddr = dev->base_addr;
+               struct arcnet_local *lp = dev->priv;
+               ARCRESET;
+       }
 
        return 0;
 }
@@ -436,7 +397,7 @@ static struct pcmcia_driver com20020_cs_driver = {
        .drv            = {
                .name   = "com20020_cs",
        },
-       .probe          = com20020_attach,
+       .probe          = com20020_probe,
        .remove         = com20020_detach,
        .id_table       = com20020_ids,
        .suspend        = com20020_suspend,
index b7ac14b..09b1176 100644 (file)
@@ -84,10 +84,10 @@ static char *version = DRV_NAME ".c " DRV_VERSION " 2002/03/23";
 /*
     PCMCIA event handlers
  */
-static void fmvj18x_config(dev_link_t *link);
-static int fmvj18x_get_hwinfo(dev_link_t *link, u_char *node_id);
-static int fmvj18x_setup_mfc(dev_link_t *link);
-static void fmvj18x_release(dev_link_t *link);
+static int fmvj18x_config(struct pcmcia_device *link);
+static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id);
+static int fmvj18x_setup_mfc(struct pcmcia_device *link);
+static void fmvj18x_release(struct pcmcia_device *link);
 static void fmvj18x_detach(struct pcmcia_device *p_dev);
 
 /*
@@ -116,7 +116,7 @@ typedef enum { MBH10302, MBH10304, TDK, CONTEC, LA501, UNGERMANN,
     driver specific data structure
 */
 typedef struct local_info_t {
-    dev_link_t link;
+       struct pcmcia_device    *p_dev;
     dev_node_t node;
     struct net_device_stats stats;
     long open_time;
@@ -228,10 +228,9 @@ typedef struct local_info_t {
 #define BANK_1U              0x24 /* bank 1 (CONFIG_1) */
 #define BANK_2U              0x28 /* bank 2 (CONFIG_1) */
 
-static int fmvj18x_attach(struct pcmcia_device *p_dev)
+static int fmvj18x_probe(struct pcmcia_device *link)
 {
     local_info_t *lp;
-    dev_link_t *link;
     struct net_device *dev;
 
     DEBUG(0, "fmvj18x_attach()\n");
@@ -241,8 +240,8 @@ static int fmvj18x_attach(struct pcmcia_device *p_dev)
     if (!dev)
        return -ENOMEM;
     lp = netdev_priv(dev);
-    link = &lp->link;
     link->priv = dev;
+    lp->p_dev = link;
 
     /* The io structure describes IO port mapping */
     link->io.NumPorts1 = 32;
@@ -257,7 +256,6 @@ static int fmvj18x_attach(struct pcmcia_device *p_dev)
 
     /* General socket configuration */
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
 
     /* The FMVJ18x specific entries in the device structure. */
@@ -274,29 +272,21 @@ static int fmvj18x_attach(struct pcmcia_device *p_dev)
 #endif
     SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    fmvj18x_config(link);
-
-    return 0;
+    return fmvj18x_config(link);
 } /* fmvj18x_attach */
 
 /*====================================================================*/
 
-static void fmvj18x_detach(struct pcmcia_device *p_dev)
+static void fmvj18x_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
     struct net_device *dev = link->priv;
 
     DEBUG(0, "fmvj18x_detach(0x%p)\n", link);
 
-    if (link->dev)
+    if (link->dev_node)
        unregister_netdev(dev);
 
-    if (link->state & DEV_CONFIG)
-       fmvj18x_release(link);
+    fmvj18x_release(link);
 
     free_netdev(dev);
 } /* fmvj18x_detach */
@@ -306,7 +296,7 @@ static void fmvj18x_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static int mfc_try_io_port(dev_link_t *link)
+static int mfc_try_io_port(struct pcmcia_device *link)
 {
     int i, ret;
     static const kio_addr_t serial_base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
@@ -318,13 +308,13 @@ static int mfc_try_io_port(dev_link_t *link)
            link->io.NumPorts2 = 0;
            printk(KERN_NOTICE "fmvj18x_cs: out of resource for serial\n");
        }
-       ret = pcmcia_request_io(link->handle, &link->io);
+       ret = pcmcia_request_io(link, &link->io);
        if (ret == CS_SUCCESS) return ret;
     }
     return ret;
 }
 
-static int ungermann_try_io_port(dev_link_t *link)
+static int ungermann_try_io_port(struct pcmcia_device *link)
 {
     int ret;
     kio_addr_t ioaddr;
@@ -334,7 +324,7 @@ static int ungermann_try_io_port(dev_link_t *link)
     */
     for (ioaddr = 0x300; ioaddr < 0x3e0; ioaddr += 0x20) {
        link->io.BasePort1 = ioaddr;
-       ret = pcmcia_request_io(link->handle, &link->io);
+       ret = pcmcia_request_io(link, &link->io);
        if (ret == CS_SUCCESS) {
            /* calculate ConfigIndex value */
            link->conf.ConfigIndex = 
@@ -345,9 +335,8 @@ static int ungermann_try_io_port(dev_link_t *link)
     return ret;        /* RequestIO failed */
 }
 
-static void fmvj18x_config(dev_link_t *link)
+static int fmvj18x_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     struct net_device *dev = link->priv;
     local_info_t *lp = netdev_priv(dev);
     tuple_t tuple;
@@ -366,42 +355,34 @@ static void fmvj18x_config(dev_link_t *link)
        registers.
     */
     tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     tuple.TupleData = (u_char *)buf;
     tuple.TupleDataMax = 64;
     tuple.TupleOffset = 0;
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
-    
-    /* Configure card */
-    link->state |= DEV_CONFIG;
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
 
     link->conf.ConfigBase = parse.config.base; 
     link->conf.Present = parse.config.rmask[0];
 
     tuple.DesiredTuple = CISTPL_FUNCE;
     tuple.TupleOffset = 0;
-    if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) {
+    if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) {
        /* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigIndex = parse.cftable_entry.index;
        tuple.DesiredTuple = CISTPL_MANFID;
-       if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS)
-           CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+       if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS)
+           CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
        else
            buf[0] = 0xffff;
        switch (le16_to_cpu(buf[0])) {
        case MANFID_TDK:
            cardtype = TDK;
-           if (le16_to_cpu(buf[1]) == PRODID_TDK_CF010) {
-               cs_status_t status;
-               pcmcia_get_status(handle, &status);
-               if (status.CardState & CS_EVENT_3VCARD)
-                   link->conf.Vcc = 33; /* inserted in 3.3V slot */
-           } else if (le16_to_cpu(buf[1]) == PRODID_TDK_GN3410
+           if (le16_to_cpu(buf[1]) == PRODID_TDK_GN3410
                        || le16_to_cpu(buf[1]) == PRODID_TDK_NP9610
                        || le16_to_cpu(buf[1]) == PRODID_TDK_MN3200) {
                /* MultiFunction Card */
@@ -429,8 +410,8 @@ static void fmvj18x_config(dev_link_t *link)
     } else {
        /* old type card */
        tuple.DesiredTuple = CISTPL_MANFID;
-       if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS)
-           CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+       if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS)
+           CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
        else
            buf[0] = 0xffff;
        switch (le16_to_cpu(buf[0])) {
@@ -461,10 +442,10 @@ static void fmvj18x_config(dev_link_t *link)
        ret = ungermann_try_io_port(link);
        if (ret != CS_SUCCESS) goto cs_failed;
     } else { 
-       CS_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io));
+       CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
     }
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
     dev->irq = link->irq.AssignedIRQ;
     dev->base_addr = link->io.BasePort1;
 
@@ -493,17 +474,17 @@ static void fmvj18x_config(dev_link_t *link)
     case CONTEC:
        tuple.DesiredTuple = CISTPL_FUNCE;
        tuple.TupleOffset = 0;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        tuple.TupleOffset = 0;
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
        if (cardtype == MBH10304) {
            /* MBH10304's CIS_FUNCE is corrupted */
            node_id = &(tuple.TupleData[5]);
            card_name = "FMV-J182";
        } else {
            while (tuple.TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID ) {
-               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
-               CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
+               CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
            }
            node_id = &(tuple.TupleData[2]);
            if( cardtype == TDK ) {
@@ -545,13 +526,12 @@ static void fmvj18x_config(dev_link_t *link)
     }
 
     lp->cardtype = cardtype;
-    link->dev = &lp->node;
-    link->state &= ~DEV_CONFIG_PENDING;
-    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+    link->dev_node = &lp->node;
+    SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
     if (register_netdev(dev) != 0) {
        printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n");
-       link->dev = NULL;
+       link->dev_node = NULL;
        goto failed;
     }
 
@@ -564,19 +544,18 @@ static void fmvj18x_config(dev_link_t *link)
     for (i = 0; i < 6; i++)
        printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
 
-    return;
+    return 0;
     
 cs_failed:
     /* All Card Services errors end up here */
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 failed:
     fmvj18x_release(link);
-    link->state &= ~DEV_CONFIG_PENDING;
-
+    return -ENODEV;
 } /* fmvj18x_config */
 /*====================================================================*/
 
-static int fmvj18x_get_hwinfo(dev_link_t *link, u_char *node_id)
+static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id)
 {
     win_req_t req;
     memreq_t mem;
@@ -587,9 +566,9 @@ static int fmvj18x_get_hwinfo(dev_link_t *link, u_char *node_id)
     req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
     req.Base = 0; req.Size = 0;
     req.AccessSpeed = 0;
-    i = pcmcia_request_window(&link->handle, &req, &link->win);
+    i = pcmcia_request_window(&link, &req, &link->win);
     if (i != CS_SUCCESS) {
-       cs_error(link->handle, RequestWindow, i);
+       cs_error(link, RequestWindow, i);
        return -1;
     }
 
@@ -623,13 +602,13 @@ static int fmvj18x_get_hwinfo(dev_link_t *link, u_char *node_id)
     iounmap(base);
     j = pcmcia_release_window(link->win);
     if (j != CS_SUCCESS)
-       cs_error(link->handle, ReleaseWindow, j);
+       cs_error(link, ReleaseWindow, j);
     return (i != 0x200) ? 0 : -1;
 
 } /* fmvj18x_get_hwinfo */
 /*====================================================================*/
 
-static int fmvj18x_setup_mfc(dev_link_t *link)
+static int fmvj18x_setup_mfc(struct pcmcia_device *link)
 {
     win_req_t req;
     memreq_t mem;
@@ -642,9 +621,9 @@ static int fmvj18x_setup_mfc(dev_link_t *link)
     req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
     req.Base = 0; req.Size = 0;
     req.AccessSpeed = 0;
-    i = pcmcia_request_window(&link->handle, &req, &link->win);
+    i = pcmcia_request_window(&link, &req, &link->win);
     if (i != CS_SUCCESS) {
-       cs_error(link->handle, RequestWindow, i);
+       cs_error(link, RequestWindow, i);
        return -1;
     }
 
@@ -666,54 +645,35 @@ static int fmvj18x_setup_mfc(dev_link_t *link)
     iounmap(base);
     j = pcmcia_release_window(link->win);
     if (j != CS_SUCCESS)
-       cs_error(link->handle, ReleaseWindow, j);
+       cs_error(link, ReleaseWindow, j);
     return 0;
 
 }
 /*====================================================================*/
 
-static void fmvj18x_release(dev_link_t *link)
+static void fmvj18x_release(struct pcmcia_device *link)
 {
-
-    DEBUG(0, "fmvj18x_release(0x%p)\n", link);
-
-    /* Don't bother checking to see if these succeed or not */
-    pcmcia_release_window(link->win);
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    
-    link->state &= ~DEV_CONFIG;
+       DEBUG(0, "fmvj18x_release(0x%p)\n", link);
+       pcmcia_disable_device(link);
 }
 
-static int fmvj18x_suspend(struct pcmcia_device *p_dev)
+static int fmvj18x_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-       }
-
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int fmvj18x_resume(struct pcmcia_device *p_dev)
+static int fmvj18x_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       fjn_reset(dev);
-                       netif_device_attach(dev);
-               }
+       if (link->open) {
+               fjn_reset(dev);
+               netif_device_attach(dev);
        }
 
        return 0;
@@ -751,7 +711,7 @@ static struct pcmcia_driver fmvj18x_cs_driver = {
        .drv            = {
                .name   = "fmvj18x_cs",
        },
-       .probe          = fmvj18x_attach,
+       .probe          = fmvj18x_probe,
        .remove         = fmvj18x_detach,
        .id_table       = fmvj18x_ids,
        .suspend        = fmvj18x_suspend,
@@ -1148,11 +1108,11 @@ static int fjn_config(struct net_device *dev, struct ifmap *map){
 static int fjn_open(struct net_device *dev)
 {
     struct local_info_t *lp = netdev_priv(dev);
-    dev_link_t *link = &lp->link;
+    struct pcmcia_device *link = lp->p_dev;
 
     DEBUG(4, "fjn_open('%s').\n", dev->name);
 
-    if (!DEV_OK(link))
+    if (!pcmcia_dev_present(link))
        return -ENODEV;
     
     link->open++;
@@ -1173,7 +1133,7 @@ static int fjn_open(struct net_device *dev)
 static int fjn_close(struct net_device *dev)
 {
     struct local_info_t *lp = netdev_priv(dev);
-    dev_link_t *link = &lp->link;
+    struct pcmcia_device *link = lp->p_dev;
     kio_addr_t ioaddr = dev->base_addr;
 
     DEBUG(4, "fjn_close('%s').\n", dev->name);
index b9c7e39..b8fe70b 100644 (file)
@@ -105,15 +105,15 @@ MODULE_LICENSE("GPL");
 
 /*====================================================================*/
 
-static void ibmtr_config(dev_link_t *link);
+static int ibmtr_config(struct pcmcia_device *link);
 static void ibmtr_hw_setup(struct net_device *dev, u_int mmiobase);
-static void ibmtr_release(dev_link_t *link);
+static void ibmtr_release(struct pcmcia_device *link);
 static void ibmtr_detach(struct pcmcia_device *p_dev);
 
 /*====================================================================*/
 
 typedef struct ibmtr_dev_t {
-    dev_link_t         link;
+       struct pcmcia_device    *p_dev;
     struct net_device  *dev;
     dev_node_t          node;
     window_handle_t     sram_win_handle;
@@ -138,12 +138,11 @@ static struct ethtool_ops netdev_ethtool_ops = {
 
 ======================================================================*/
 
-static int ibmtr_attach(struct pcmcia_device *p_dev)
+static int ibmtr_attach(struct pcmcia_device *link)
 {
     ibmtr_dev_t *info;
-    dev_link_t *link;
     struct net_device *dev;
-    
+
     DEBUG(0, "ibmtr_attach()\n");
 
     /* Create new token-ring device */
@@ -156,7 +155,7 @@ static int ibmtr_attach(struct pcmcia_device *p_dev)
        return -ENOMEM;
     }
 
-    link = &info->link;
+    info->p_dev = link;
     link->priv = info;
     info->ti = netdev_priv(dev);
 
@@ -167,21 +166,14 @@ static int ibmtr_attach(struct pcmcia_device *p_dev)
     link->irq.IRQInfo1 = IRQ_LEVEL_ID;
     link->irq.Handler = &tok_interrupt;
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
     link->conf.Present = PRESENT_OPTION;
 
     link->irq.Instance = info->dev = dev;
-    
-    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
-
-    link->handle = p_dev;
-    p_dev->instance = link;
 
-    link->state |= DEV_PRESENT;
-    ibmtr_config(link);
+    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
 
-    return 0;
+    return ibmtr_config(link);
 } /* ibmtr_attach */
 
 /*======================================================================
@@ -193,23 +185,22 @@ static int ibmtr_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void ibmtr_detach(struct pcmcia_device *p_dev)
+static void ibmtr_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
     struct ibmtr_dev_t *info = link->priv;
     struct net_device *dev = info->dev;
 
     DEBUG(0, "ibmtr_detach(0x%p)\n", link);
 
-    if (link->dev)
+    if (link->dev_node)
        unregister_netdev(dev);
 
     {
        struct tok_info *ti = netdev_priv(dev);
        del_timer_sync(&(ti->tr_timer));
     }
-    if (link->state & DEV_CONFIG)
-        ibmtr_release(link);
+
+    ibmtr_release(link);
 
     free_netdev(dev);
     kfree(info);
@@ -226,9 +217,8 @@ static void ibmtr_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void ibmtr_config(dev_link_t *link)
+static int ibmtr_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     ibmtr_dev_t *info = link->priv;
     struct net_device *dev = info->dev;
     struct tok_info *ti = netdev_priv(dev);
@@ -246,29 +236,25 @@ static void ibmtr_config(dev_link_t *link)
     tuple.TupleDataMax = 64;
     tuple.TupleOffset = 0;
     tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
-
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
     link->conf.ConfigIndex = 0x61;
 
     /* Determine if this is PRIMARY or ALTERNATE. */
 
     /* Try PRIMARY card at 0xA20-0xA23 */
     link->io.BasePort1 = 0xA20;
-    i = pcmcia_request_io(link->handle, &link->io);
+    i = pcmcia_request_io(link, &link->io);
     if (i != CS_SUCCESS) {
        /* Couldn't get 0xA20-0xA23.  Try ALTERNATE at 0xA24-0xA27. */
        link->io.BasePort1 = 0xA24;
-       CS_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io));
+       CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
     }
     dev->base_addr = link->io.BasePort1;
 
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
     dev->irq = link->irq.AssignedIRQ;
     ti->irq = link->irq.AssignedIRQ;
     ti->global_int_enable=GLOBAL_INT_ENABLE+((dev->irq==9) ? 2 : dev->irq);
@@ -279,7 +265,7 @@ static void ibmtr_config(dev_link_t *link)
     req.Base = 0; 
     req.Size = 0x2000;
     req.AccessSpeed = 250;
-    CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &link->win));
+    CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win));
 
     mem.CardOffset = mmiobase;
     mem.Page = 0;
@@ -292,7 +278,7 @@ static void ibmtr_config(dev_link_t *link)
     req.Base = 0;
     req.Size = sramsize * 1024;
     req.AccessSpeed = 250;
-    CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &info->sram_win_handle));
+    CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &info->sram_win_handle));
 
     mem.CardOffset = srambase;
     mem.Page = 0;
@@ -302,21 +288,20 @@ static void ibmtr_config(dev_link_t *link)
     ti->sram_virt = ioremap(req.Base, req.Size);
     ti->sram_phys = req.Base;
 
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
     /*  Set up the Token-Ring Controller Configuration Register and
         turn on the card.  Check the "Local Area Network Credit Card
         Adapters Technical Reference"  SC30-3585 for this info.  */
     ibmtr_hw_setup(dev, mmiobase);
 
-    link->dev = &info->node;
-    link->state &= ~DEV_CONFIG_PENDING;
-    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+    link->dev_node = &info->node;
+    SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
     i = ibmtr_probe_card(dev);
     if (i != 0) {
        printk(KERN_NOTICE "ibmtr_cs: register_netdev() failed\n");
-       link->dev = NULL;
+       link->dev_node = NULL;
        goto failed;
     }
 
@@ -330,12 +315,13 @@ static void ibmtr_config(dev_link_t *link)
     for (i = 0; i < TR_ALEN; i++)
         printk("%02X", dev->dev_addr[i]);
     printk("\n");
-    return;
+    return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 failed:
     ibmtr_release(link);
+    return -ENODEV;
 } /* ibmtr_config */
 
 /*======================================================================
@@ -346,56 +332,41 @@ failed:
 
 ======================================================================*/
 
-static void ibmtr_release(dev_link_t *link)
+static void ibmtr_release(struct pcmcia_device *link)
 {
-    ibmtr_dev_t *info = link->priv;
-    struct net_device *dev = info->dev;
-
-    DEBUG(0, "ibmtr_release(0x%p)\n", link);
+       ibmtr_dev_t *info = link->priv;
+       struct net_device *dev = info->dev;
 
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    if (link->win) {
-       struct tok_info *ti = netdev_priv(dev);
-       iounmap(ti->mmio);
-       pcmcia_release_window(link->win);
-       pcmcia_release_window(info->sram_win_handle);
-    }
+       DEBUG(0, "ibmtr_release(0x%p)\n", link);
 
-    link->state &= ~DEV_CONFIG;
+       if (link->win) {
+               struct tok_info *ti = netdev_priv(dev);
+               iounmap(ti->mmio);
+               pcmcia_release_window(info->sram_win_handle);
+       }
+       pcmcia_disable_device(link);
 }
 
-static int ibmtr_suspend(struct pcmcia_device *p_dev)
+static int ibmtr_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        ibmtr_dev_t *info = link->priv;
        struct net_device *dev = info->dev;
 
-       link->state |= DEV_SUSPEND;
-        if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-        }
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int ibmtr_resume(struct pcmcia_device *p_dev)
+static int ibmtr_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        ibmtr_dev_t *info = link->priv;
        struct net_device *dev = info->dev;
 
-       link->state &= ~DEV_SUSPEND;
-        if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       ibmtr_probe(dev);       /* really? */
-                       netif_device_attach(dev);
-               }
-        }
+       if (link->open) {
+               ibmtr_probe(dev);       /* really? */
+               netif_device_attach(dev);
+       }
 
        return 0;
 }
index 787176c..4260c21 100644 (file)
@@ -362,7 +362,7 @@ typedef struct _mace_statistics {
 } mace_statistics;
 
 typedef struct _mace_private {
-    dev_link_t link;
+       struct pcmcia_device    *p_dev;
     dev_node_t node;
     struct net_device_stats linux_stats; /* Linux statistics counters */
     mace_statistics mace_stats; /* MACE chip statistics counters */
@@ -417,8 +417,8 @@ INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
 Function Prototypes
 ---------------------------------------------------------------------------- */
 
-static void nmclan_config(dev_link_t *link);
-static void nmclan_release(dev_link_t *link);
+static int nmclan_config(struct pcmcia_device *link);
+static void nmclan_release(struct pcmcia_device *link);
 
 static void nmclan_reset(struct net_device *dev);
 static int mace_config(struct net_device *dev, struct ifmap *map);
@@ -443,10 +443,9 @@ nmclan_attach
        Services.
 ---------------------------------------------------------------------------- */
 
-static int nmclan_attach(struct pcmcia_device *p_dev)
+static int nmclan_probe(struct pcmcia_device *link)
 {
     mace_private *lp;
-    dev_link_t *link;
     struct net_device *dev;
 
     DEBUG(0, "nmclan_attach()\n");
@@ -457,7 +456,7 @@ static int nmclan_attach(struct pcmcia_device *p_dev)
     if (!dev)
            return -ENOMEM;
     lp = netdev_priv(dev);
-    link = &lp->link;
+    lp->p_dev = link;
     link->priv = dev;
     
     spin_lock_init(&lp->bank_lock);
@@ -469,7 +468,6 @@ static int nmclan_attach(struct pcmcia_device *p_dev)
     link->irq.Handler = &mace_interrupt;
     link->irq.Instance = dev;
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
     link->conf.ConfigIndex = 1;
     link->conf.Present = PRESENT_OPTION;
@@ -489,13 +487,7 @@ static int nmclan_attach(struct pcmcia_device *p_dev)
     dev->watchdog_timeo = TX_TIMEOUT;
 #endif
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    nmclan_config(link);
-
-    return 0;
+    return nmclan_config(link);
 } /* nmclan_attach */
 
 /* ----------------------------------------------------------------------------
@@ -506,18 +498,16 @@ nmclan_detach
        when the device is released.
 ---------------------------------------------------------------------------- */
 
-static void nmclan_detach(struct pcmcia_device *p_dev)
+static void nmclan_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
     struct net_device *dev = link->priv;
 
     DEBUG(0, "nmclan_detach(0x%p)\n", link);
 
-    if (link->dev)
+    if (link->dev_node)
        unregister_netdev(dev);
 
-    if (link->state & DEV_CONFIG)
-       nmclan_release(link);
+    nmclan_release(link);
 
     free_netdev(dev);
 } /* nmclan_detach */
@@ -661,9 +651,8 @@ nmclan_config
 #define CS_CHECK(fn, ret) \
   do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void nmclan_config(dev_link_t *link)
+static int nmclan_config(struct pcmcia_device *link)
 {
-  client_handle_t handle = link->handle;
   struct net_device *dev = link->priv;
   mace_private *lp = netdev_priv(dev);
   tuple_t tuple;
@@ -679,17 +668,14 @@ static void nmclan_config(dev_link_t *link)
   tuple.TupleDataMax = 64;
   tuple.TupleOffset = 0;
   tuple.DesiredTuple = CISTPL_CONFIG;
-  CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-  CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-  CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+  CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+  CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+  CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
   link->conf.ConfigBase = parse.config.base;
 
-  /* Configure card */
-  link->state |= DEV_CONFIG;
-
-  CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io));
-  CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
-  CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+  CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
+  CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+  CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
   dev->irq = link->irq.AssignedIRQ;
   dev->base_addr = link->io.BasePort1;
 
@@ -700,8 +686,8 @@ static void nmclan_config(dev_link_t *link)
   tuple.TupleData = buf;
   tuple.TupleDataMax = 64;
   tuple.TupleOffset = 0;
-  CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-  CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+  CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+  CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
   memcpy(dev->dev_addr, tuple.TupleData, ETHER_ADDR_LEN);
 
   /* Verify configuration by reading the MACE ID. */
@@ -716,8 +702,7 @@ static void nmclan_config(dev_link_t *link)
     } else {
       printk(KERN_NOTICE "nmclan_cs: mace id not found: %x %x should"
             " be 0x40 0x?9\n", sig[0], sig[1]);
-      link->state &= ~DEV_CONFIG_PENDING;
-      return;
+      return -ENODEV;
     }
   }
 
@@ -730,14 +715,13 @@ static void nmclan_config(dev_link_t *link)
   else
     printk(KERN_NOTICE "nmclan_cs: invalid if_port requested\n");
 
-  link->dev = &lp->node;
-  link->state &= ~DEV_CONFIG_PENDING;
-  SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+  link->dev_node = &lp->node;
+  SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
   i = register_netdev(dev);
   if (i != 0) {
     printk(KERN_NOTICE "nmclan_cs: register_netdev() failed\n");
-    link->dev = NULL;
+    link->dev_node = NULL;
     goto failed;
   }
 
@@ -747,14 +731,13 @@ static void nmclan_config(dev_link_t *link)
         dev->name, dev->base_addr, dev->irq, if_names[dev->if_port]);
   for (i = 0; i < 6; i++)
       printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
-  return;
+  return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 failed:
-    nmclan_release(link);
-    return;
-
+       nmclan_release(link);
+       return -ENODEV;
 } /* nmclan_config */
 
 /* ----------------------------------------------------------------------------
@@ -763,46 +746,29 @@ nmclan_release
        net device, and release the PCMCIA configuration.  If the device
        is still open, this will be postponed until it is closed.
 ---------------------------------------------------------------------------- */
-static void nmclan_release(dev_link_t *link)
+static void nmclan_release(struct pcmcia_device *link)
 {
-
-  DEBUG(0, "nmclan_release(0x%p)\n", link);
-
-  pcmcia_release_configuration(link->handle);
-  pcmcia_release_io(link->handle, &link->io);
-  pcmcia_release_irq(link->handle, &link->irq);
-
-  link->state &= ~DEV_CONFIG;
+       DEBUG(0, "nmclan_release(0x%p)\n", link);
+       pcmcia_disable_device(link);
 }
 
-static int nmclan_suspend(struct pcmcia_device *p_dev)
+static int nmclan_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-       }
-
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int nmclan_resume(struct pcmcia_device *p_dev)
+static int nmclan_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       nmclan_reset(dev);
-                       netif_device_attach(dev);
-               }
+       if (link->open) {
+               nmclan_reset(dev);
+               netif_device_attach(dev);
        }
 
        return 0;
@@ -818,7 +784,7 @@ static void nmclan_reset(struct net_device *dev)
   mace_private *lp = netdev_priv(dev);
 
 #if RESET_XILINX
-  dev_link_t *link = &lp->link;
+  struct pcmcia_device *link = &lp->link;
   conf_reg_t reg;
   u_long OrigCorValue; 
 
@@ -827,7 +793,7 @@ static void nmclan_reset(struct net_device *dev)
   reg.Action = CS_READ;
   reg.Offset = CISREG_COR;
   reg.Value = 0;
-  pcmcia_access_configuration_register(link->handle, &reg);
+  pcmcia_access_configuration_register(link, &reg);
   OrigCorValue = reg.Value;
 
   /* Reset Xilinx */
@@ -836,12 +802,12 @@ static void nmclan_reset(struct net_device *dev)
   DEBUG(1, "nmclan_reset: OrigCorValue=0x%lX, resetting...\n",
        OrigCorValue);
   reg.Value = COR_SOFT_RESET;
-  pcmcia_access_configuration_register(link->handle, &reg);
+  pcmcia_access_configuration_register(link, &reg);
   /* Need to wait for 20 ms for PCMCIA to finish reset. */
 
   /* Restore original COR configuration index */
   reg.Value = COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK);
-  pcmcia_access_configuration_register(link->handle, &reg);
+  pcmcia_access_configuration_register(link, &reg);
   /* Xilinx is now completely reset along with the MACE chip. */
   lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
 
@@ -885,9 +851,9 @@ static int mace_open(struct net_device *dev)
 {
   kio_addr_t ioaddr = dev->base_addr;
   mace_private *lp = netdev_priv(dev);
-  dev_link_t *link = &lp->link;
+  struct pcmcia_device *link = lp->p_dev;
 
-  if (!DEV_OK(link))
+  if (!pcmcia_dev_present(link))
     return -ENODEV;
 
   link->open++;
@@ -908,7 +874,7 @@ static int mace_close(struct net_device *dev)
 {
   kio_addr_t ioaddr = dev->base_addr;
   mace_private *lp = netdev_priv(dev);
-  dev_link_t *link = &lp->link;
+  struct pcmcia_device *link = lp->p_dev;
 
   DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
 
@@ -963,12 +929,12 @@ mace_start_xmit
 static void mace_tx_timeout(struct net_device *dev)
 {
   mace_private *lp = netdev_priv(dev);
-  dev_link_t *link = &lp->link;
+  struct pcmcia_device *link = lp->p_dev;
 
   printk(KERN_NOTICE "%s: transmit timed out -- ", dev->name);
 #if RESET_ON_TIMEOUT
   printk("resetting card\n");
-  pcmcia_reset_card(link->handle, NULL);
+  pcmcia_reset_card(link, NULL);
 #else /* #if RESET_ON_TIMEOUT */
   printk("NOT resetting card\n");
 #endif /* #if RESET_ON_TIMEOUT */
@@ -1635,7 +1601,7 @@ static struct pcmcia_driver nmclan_cs_driver = {
        .drv            = {
                .name   = "nmclan_cs",
        },
-       .probe          = nmclan_attach,
+       .probe          = nmclan_probe,
        .remove         = nmclan_detach,
        .id_table       = nmclan_ids,
        .suspend        = nmclan_suspend,
index b46e5f7..506e777 100644 (file)
@@ -103,8 +103,8 @@ module_param_array(hw_addr, int, NULL, 0);
 /*====================================================================*/
 
 static void mii_phy_probe(struct net_device *dev);
-static void pcnet_config(dev_link_t *link);
-static void pcnet_release(dev_link_t *link);
+static int pcnet_config(struct pcmcia_device *link);
+static void pcnet_release(struct pcmcia_device *link);
 static int pcnet_open(struct net_device *dev);
 static int pcnet_close(struct net_device *dev);
 static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -113,9 +113,9 @@ static irqreturn_t ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs);
 static void ei_watchdog(u_long arg);
 static void pcnet_reset_8390(struct net_device *dev);
 static int set_config(struct net_device *dev, struct ifmap *map);
-static int setup_shmem_window(dev_link_t *link, int start_pg,
+static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
                              int stop_pg, int cm_offset);
-static int setup_dma_config(dev_link_t *link, int start_pg,
+static int setup_dma_config(struct pcmcia_device *link, int start_pg,
                            int stop_pg);
 
 static void pcnet_detach(struct pcmcia_device *p_dev);
@@ -214,7 +214,7 @@ static hw_info_t dl10019_info = { 0, 0, 0, 0, IS_DL10019|HAS_MII };
 static hw_info_t dl10022_info = { 0, 0, 0, 0, IS_DL10022|HAS_MII };
 
 typedef struct pcnet_dev_t {
-    dev_link_t         link;
+       struct pcmcia_device    *p_dev;
     dev_node_t         node;
     u_int              flags;
     void               __iomem *base;
@@ -240,10 +240,9 @@ static inline pcnet_dev_t *PRIV(struct net_device *dev)
 
 ======================================================================*/
 
-static int pcnet_probe(struct pcmcia_device *p_dev)
+static int pcnet_probe(struct pcmcia_device *link)
 {
     pcnet_dev_t *info;
-    dev_link_t *link;
     struct net_device *dev;
 
     DEBUG(0, "pcnet_attach()\n");
@@ -252,7 +251,7 @@ static int pcnet_probe(struct pcmcia_device *p_dev)
     dev = __alloc_ei_netdev(sizeof(pcnet_dev_t));
     if (!dev) return -ENOMEM;
     info = PRIV(dev);
-    link = &info->link;
+    info->p_dev = link;
     link->priv = dev;
 
     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
@@ -265,13 +264,7 @@ static int pcnet_probe(struct pcmcia_device *p_dev)
     dev->stop = &pcnet_close;
     dev->set_config = &set_config;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    pcnet_config(link);
-
-    return 0;
+    return pcnet_config(link);
 } /* pcnet_attach */
 
 /*======================================================================
@@ -283,18 +276,16 @@ static int pcnet_probe(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void pcnet_detach(struct pcmcia_device *p_dev)
+static void pcnet_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
        DEBUG(0, "pcnet_detach(0x%p)\n", link);
 
-       if (link->dev)
+       if (link->dev_node)
                unregister_netdev(dev);
 
-       if (link->state & DEV_CONFIG)
-               pcnet_release(link);
+       pcnet_release(link);
 
        free_netdev(dev);
 } /* pcnet_detach */
@@ -306,7 +297,7 @@ static void pcnet_detach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static hw_info_t *get_hwinfo(dev_link_t *link)
+static hw_info_t *get_hwinfo(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     win_req_t req;
@@ -318,9 +309,9 @@ static hw_info_t *get_hwinfo(dev_link_t *link)
     req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
     req.Base = 0; req.Size = 0;
     req.AccessSpeed = 0;
-    i = pcmcia_request_window(&link->handle, &req, &link->win);
+    i = pcmcia_request_window(&link, &req, &link->win);
     if (i != CS_SUCCESS) {
-       cs_error(link->handle, RequestWindow, i);
+       cs_error(link, RequestWindow, i);
        return NULL;
     }
 
@@ -343,7 +334,7 @@ static hw_info_t *get_hwinfo(dev_link_t *link)
     iounmap(virt);
     j = pcmcia_release_window(link->win);
     if (j != CS_SUCCESS)
-       cs_error(link->handle, ReleaseWindow, j);
+       cs_error(link, ReleaseWindow, j);
     return (i < NR_INFO) ? hw_info+i : NULL;
 } /* get_hwinfo */
 
@@ -355,7 +346,7 @@ static hw_info_t *get_hwinfo(dev_link_t *link)
 
 ======================================================================*/
 
-static hw_info_t *get_prom(dev_link_t *link)
+static hw_info_t *get_prom(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     kio_addr_t ioaddr = dev->base_addr;
@@ -409,7 +400,7 @@ static hw_info_t *get_prom(dev_link_t *link)
 
 ======================================================================*/
 
-static hw_info_t *get_dl10019(dev_link_t *link)
+static hw_info_t *get_dl10019(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     int i;
@@ -431,7 +422,7 @@ static hw_info_t *get_dl10019(dev_link_t *link)
 
 ======================================================================*/
 
-static hw_info_t *get_ax88190(dev_link_t *link)
+static hw_info_t *get_ax88190(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     kio_addr_t ioaddr = dev->base_addr;
@@ -464,7 +455,7 @@ static hw_info_t *get_ax88190(dev_link_t *link)
 
 ======================================================================*/
 
-static hw_info_t *get_hwired(dev_link_t *link)
+static hw_info_t *get_hwired(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     int i;
@@ -491,7 +482,7 @@ static hw_info_t *get_hwired(dev_link_t *link)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static int try_io_port(dev_link_t *link)
+static int try_io_port(struct pcmcia_device *link)
 {
     int j, ret;
     if (link->io.NumPorts1 == 32) {
@@ -512,18 +503,17 @@ static int try_io_port(dev_link_t *link)
        for (j = 0; j < 0x400; j += 0x20) {
            link->io.BasePort1 = j ^ 0x300;
            link->io.BasePort2 = (j ^ 0x300) + 0x10;
-           ret = pcmcia_request_io(link->handle, &link->io);
+           ret = pcmcia_request_io(link, &link->io);
            if (ret == CS_SUCCESS) return ret;
        }
        return ret;
     } else {
-       return pcmcia_request_io(link->handle, &link->io);
+       return pcmcia_request_io(link, &link->io);
     }
 }
 
-static void pcnet_config(dev_link_t *link)
+static int pcnet_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     struct net_device *dev = link->priv;
     pcnet_dev_t *info = PRIV(dev);
     tuple_t tuple;
@@ -531,7 +521,6 @@ static void pcnet_config(dev_link_t *link)
     int i, last_ret, last_fn, start_pg, stop_pg, cm_offset;
     int manfid = 0, prodid = 0, has_shmem = 0;
     u_short buf[64];
-    config_info_t conf;
     hw_info_t *hw_info;
 
     DEBUG(0, "pcnet_config(0x%p)\n", link);
@@ -541,36 +530,29 @@ static void pcnet_config(dev_link_t *link)
     tuple.TupleDataMax = sizeof(buf);
     tuple.TupleOffset = 0;
     tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present = parse.config.rmask[0];
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
-    /* Look up current Vcc */
-    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
-    link->conf.Vcc = conf.Vcc;
-
     tuple.DesiredTuple = CISTPL_MANFID;
     tuple.Attributes = TUPLE_RETURN_COMMON;
-    if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
-       (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) {
+    if ((pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) &&
+       (pcmcia_get_tuple_data(link, &tuple) == CS_SUCCESS)) {
        manfid = le16_to_cpu(buf[0]);
        prodid = le16_to_cpu(buf[1]);
     }
     
     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
     tuple.Attributes = 0;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     while (last_ret == CS_SUCCESS) {
        cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
        cistpl_io_t *io = &(parse.cftable_entry.io);
        
-       if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-                       pcmcia_parse_tuple(handle, &tuple, &parse) != 0 ||
+       if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+                       pcmcia_parse_tuple(link, &tuple, &parse) != 0 ||
                        cfg->index == 0 || cfg->io.nwin == 0)
                goto next_entry;
        
@@ -594,14 +576,14 @@ static void pcnet_config(dev_link_t *link)
            if (last_ret == CS_SUCCESS) break;
        }
     next_entry:
-       last_ret = pcmcia_get_next_tuple(handle, &tuple);
+       last_ret = pcmcia_get_next_tuple(link, &tuple);
     }
     if (last_ret != CS_SUCCESS) {
-       cs_error(handle, RequestIO, last_ret);
+       cs_error(link, RequestIO, last_ret);
        goto failed;
     }
 
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
     
     if (link->io.NumPorts2 == 8) {
        link->conf.Attributes |= CONF_ENABLE_SPKR;
@@ -611,7 +593,7 @@ static void pcnet_config(dev_link_t *link)
        (prodid == PRODID_IBM_HOME_AND_AWAY))
        link->conf.ConfigIndex |= 0x10;
     
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
     dev->irq = link->irq.AssignedIRQ;
     dev->base_addr = link->io.BasePort1;
     if (info->flags & HAS_MISC_REG) {
@@ -679,9 +661,8 @@ static void pcnet_config(dev_link_t *link)
            info->eth_phy = 0;
     }
 
-    link->dev = &info->node;
-    link->state &= ~DEV_CONFIG_PENDING;
-    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+    link->dev_node = &info->node;
+    SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
     dev->poll_controller = ei_poll;
@@ -689,7 +670,7 @@ static void pcnet_config(dev_link_t *link)
 
     if (register_netdev(dev) != 0) {
        printk(KERN_NOTICE "pcnet_cs: register_netdev() failed\n");
-       link->dev = NULL;
+       link->dev_node = NULL;
        goto failed;
     }
 
@@ -712,14 +693,13 @@ static void pcnet_config(dev_link_t *link)
     printk(" hw_addr ");
     for (i = 0; i < 6; i++)
        printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
-    return;
+    return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 failed:
     pcnet_release(link);
-    link->state &= ~DEV_CONFIG_PENDING;
-    return;
+    return -ENODEV;
 } /* pcnet_config */
 
 /*======================================================================
@@ -730,21 +710,16 @@ failed:
 
 ======================================================================*/
 
-static void pcnet_release(dev_link_t *link)
+static void pcnet_release(struct pcmcia_device *link)
 {
-    pcnet_dev_t *info = PRIV(link->priv);
+       pcnet_dev_t *info = PRIV(link->priv);
 
-    DEBUG(0, "pcnet_release(0x%p)\n", link);
+       DEBUG(0, "pcnet_release(0x%p)\n", link);
 
-    if (info->flags & USE_SHMEM) {
-       iounmap(info->base);
-       pcmcia_release_window(link->win);
-    }
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
+       if (info->flags & USE_SHMEM)
+               iounmap(info->base);
 
-    link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
 }
 
 /*======================================================================
@@ -756,34 +731,24 @@ static void pcnet_release(dev_link_t *link)
 
 ======================================================================*/
 
-static int pcnet_suspend(struct pcmcia_device *p_dev)
+static int pcnet_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-       }
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int pcnet_resume(struct pcmcia_device *p_dev)
+static int pcnet_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       pcnet_reset_8390(dev);
-                       NS8390_init(dev, 1);
-                       netif_device_attach(dev);
-               }
+       if (link->open) {
+               pcnet_reset_8390(dev);
+               NS8390_init(dev, 1);
+               netif_device_attach(dev);
        }
 
        return 0;
@@ -1023,11 +988,11 @@ static void mii_phy_probe(struct net_device *dev)
 static int pcnet_open(struct net_device *dev)
 {
     pcnet_dev_t *info = PRIV(dev);
-    dev_link_t *link = &info->link;
-    
+    struct pcmcia_device *link = info->p_dev;
+
     DEBUG(2, "pcnet_open('%s')\n", dev->name);
 
-    if (!DEV_OK(link))
+    if (!pcmcia_dev_present(link))
        return -ENODEV;
 
     link->open++;
@@ -1051,7 +1016,7 @@ static int pcnet_open(struct net_device *dev)
 static int pcnet_close(struct net_device *dev)
 {
     pcnet_dev_t *info = PRIV(dev);
-    dev_link_t *link = &info->link;
+    struct pcmcia_device *link = info->p_dev;
 
     DEBUG(2, "pcnet_close('%s')\n", dev->name);
 
@@ -1429,7 +1394,7 @@ static void dma_block_output(struct net_device *dev, int count,
 
 /*====================================================================*/
 
-static int setup_dma_config(dev_link_t *link, int start_pg,
+static int setup_dma_config(struct pcmcia_device *link, int start_pg,
                            int stop_pg)
 {
     struct net_device *dev = link->priv;
@@ -1532,7 +1497,7 @@ static void shmem_block_output(struct net_device *dev, int count,
 
 /*====================================================================*/
 
-static int setup_shmem_window(dev_link_t *link, int start_pg,
+static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
                              int stop_pg, int cm_offset)
 {
     struct net_device *dev = link->priv;
@@ -1554,7 +1519,7 @@ static int setup_shmem_window(dev_link_t *link, int start_pg,
     req.Attributes |= WIN_USE_WAIT;
     req.Base = 0; req.Size = window_size;
     req.AccessSpeed = mem_speed;
-    CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &link->win));
+    CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win));
 
     mem.CardOffset = (start_pg << 8) + cm_offset;
     offset = mem.CardOffset % window_size;
@@ -1595,7 +1560,7 @@ static int setup_shmem_window(dev_link_t *link, int start_pg,
     return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 failed:
     return 1;
 }
index 8839c4f..e74bf50 100644 (file)
@@ -49,6 +49,7 @@
 #include <pcmcia/cisreg.h>
 #include <pcmcia/ciscode.h>
 #include <pcmcia/ds.h>
+#include <pcmcia/ss.h>
 
 #include <asm/io.h>
 #include <asm/system.h>
@@ -103,7 +104,7 @@ static const char *version =
 #define MEMORY_WAIT_TIME               8
 
 struct smc_private {
-    dev_link_t                 link;
+       struct pcmcia_device    *p_dev;
     spinlock_t                 lock;
     u_short                    manfid;
     u_short                    cardid;
@@ -278,8 +279,8 @@ enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002,
 /*====================================================================*/
 
 static void smc91c92_detach(struct pcmcia_device *p_dev);
-static void smc91c92_config(dev_link_t *link);
-static void smc91c92_release(dev_link_t *link);
+static int smc91c92_config(struct pcmcia_device *link);
+static void smc91c92_release(struct pcmcia_device *link);
 
 static int smc_open(struct net_device *dev);
 static int smc_close(struct net_device *dev);
@@ -308,10 +309,9 @@ static struct ethtool_ops ethtool_ops;
 
 ======================================================================*/
 
-static int smc91c92_attach(struct pcmcia_device *p_dev)
+static int smc91c92_probe(struct pcmcia_device *link)
 {
     struct smc_private *smc;
-    dev_link_t *link;
     struct net_device *dev;
 
     DEBUG(0, "smc91c92_attach()\n");
@@ -321,7 +321,7 @@ static int smc91c92_attach(struct pcmcia_device *p_dev)
     if (!dev)
        return -ENOMEM;
     smc = netdev_priv(dev);
-    link = &smc->link;
+    smc->p_dev = link;
     link->priv = dev;
 
     spin_lock_init(&smc->lock);
@@ -333,7 +333,6 @@ static int smc91c92_attach(struct pcmcia_device *p_dev)
     link->irq.Handler = &smc_interrupt;
     link->irq.Instance = dev;
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
 
     /* The SMC91c92-specific entries in the device structure. */
@@ -357,13 +356,7 @@ static int smc91c92_attach(struct pcmcia_device *p_dev)
     smc->mii_if.phy_id_mask = 0x1f;
     smc->mii_if.reg_num_mask = 0x1f;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    smc91c92_config(link);
-
-    return 0;
+    return smc91c92_config(link);
 } /* smc91c92_attach */
 
 /*======================================================================
@@ -375,18 +368,16 @@ static int smc91c92_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void smc91c92_detach(struct pcmcia_device *p_dev)
+static void smc91c92_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
     struct net_device *dev = link->priv;
 
     DEBUG(0, "smc91c92_detach(0x%p)\n", link);
 
-    if (link->dev)
+    if (link->dev_node)
        unregister_netdev(dev);
 
-    if (link->state & DEV_CONFIG)
-       smc91c92_release(link);
+    smc91c92_release(link);
 
     free_netdev(dev);
 } /* smc91c92_detach */
@@ -414,7 +405,7 @@ static int cvt_ascii_address(struct net_device *dev, char *s)
 
 /*====================================================================*/
 
-static int first_tuple(client_handle_t handle, tuple_t *tuple,
+static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                cisparse_t *parse)
 {
        int i;
@@ -425,7 +416,7 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple,
        return pcmcia_parse_tuple(handle, tuple, parse);
 }
 
-static int next_tuple(client_handle_t handle, tuple_t *tuple,
+static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
                cisparse_t *parse)
 {
        int i;
@@ -447,7 +438,7 @@ static int next_tuple(client_handle_t handle, tuple_t *tuple,
 
 ======================================================================*/
 
-static int mhz_3288_power(dev_link_t *link)
+static int mhz_3288_power(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     struct smc_private *smc = netdev_priv(dev);
@@ -469,7 +460,7 @@ static int mhz_3288_power(dev_link_t *link)
     return 0;
 }
 
-static int mhz_mfc_config(dev_link_t *link)
+static int mhz_mfc_config(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     struct smc_private *smc = netdev_priv(dev);
@@ -504,7 +495,7 @@ static int mhz_mfc_config(dev_link_t *link)
     tuple->TupleDataMax = 255;
     tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
 
-    i = first_tuple(link->handle, tuple, parse);
+    i = first_tuple(link, tuple, parse);
     /* The Megahertz combo cards have modem-like CIS entries, so
        we have to explicitly try a bunch of port combinations. */
     while (i == CS_SUCCESS) {
@@ -513,11 +504,11 @@ static int mhz_mfc_config(dev_link_t *link)
        for (k = 0; k < 0x400; k += 0x10) {
            if (k & 0x80) continue;
            link->io.BasePort1 = k ^ 0x300;
-           i = pcmcia_request_io(link->handle, &link->io);
+           i = pcmcia_request_io(link, &link->io);
            if (i == CS_SUCCESS) break;
        }
        if (i == CS_SUCCESS) break;
-       i = next_tuple(link->handle, tuple, parse);
+       i = next_tuple(link, tuple, parse);
     }
     if (i != CS_SUCCESS)
        goto free_cfg_mem;
@@ -527,7 +518,7 @@ static int mhz_mfc_config(dev_link_t *link)
     req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
     req.Base = req.Size = 0;
     req.AccessSpeed = 0;
-    i = pcmcia_request_window(&link->handle, &req, &link->win);
+    i = pcmcia_request_window(&link, &req, &link->win);
     if (i != CS_SUCCESS)
        goto free_cfg_mem;
     smc->base = ioremap(req.Base, req.Size);
@@ -546,9 +537,8 @@ free_cfg_mem:
     return i;
 }
 
-static int mhz_setup(dev_link_t *link)
+static int mhz_setup(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     struct net_device *dev = link->priv;
     struct smc_cfg_mem *cfg_mem;
     tuple_t *tuple;
@@ -571,13 +561,13 @@ static int mhz_setup(dev_link_t *link)
     /* Read the station address from the CIS.  It is stored as the last
        (fourth) string in the Version 1 Version/ID tuple. */
     tuple->DesiredTuple = CISTPL_VERS_1;
-    if (first_tuple(handle, tuple, parse) != CS_SUCCESS) {
+    if (first_tuple(link, tuple, parse) != CS_SUCCESS) {
        rc = -1;
        goto free_cfg_mem;
     }
     /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
-    if (next_tuple(handle, tuple, parse) != CS_SUCCESS)
-       first_tuple(handle, tuple, parse);
+    if (next_tuple(link, tuple, parse) != CS_SUCCESS)
+       first_tuple(link, tuple, parse);
     if (parse->version_1.ns > 3) {
        station_addr = parse->version_1.str + parse->version_1.ofs[3];
        if (cvt_ascii_address(dev, station_addr) == 0) {
@@ -588,11 +578,11 @@ static int mhz_setup(dev_link_t *link)
 
     /* Another possibility: for the EM3288, in a special tuple */
     tuple->DesiredTuple = 0x81;
-    if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS) {
+    if (pcmcia_get_first_tuple(link, tuple) != CS_SUCCESS) {
        rc = -1;
        goto free_cfg_mem;
     }
-    if (pcmcia_get_tuple_data(handle, tuple) != CS_SUCCESS) {
+    if (pcmcia_get_tuple_data(link, tuple) != CS_SUCCESS) {
        rc = -1;
        goto free_cfg_mem;
     }
@@ -616,7 +606,7 @@ free_cfg_mem:
 
 ======================================================================*/
 
-static void mot_config(dev_link_t *link)
+static void mot_config(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     struct smc_private *smc = netdev_priv(dev);
@@ -637,7 +627,7 @@ static void mot_config(dev_link_t *link)
     mdelay(100);
 }
 
-static int mot_setup(dev_link_t *link)
+static int mot_setup(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     kio_addr_t ioaddr = dev->base_addr;
@@ -671,7 +661,7 @@ static int mot_setup(dev_link_t *link)
 
 /*====================================================================*/
 
-static int smc_config(dev_link_t *link)
+static int smc_config(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     struct smc_cfg_mem *cfg_mem;
@@ -696,16 +686,16 @@ static int smc_config(dev_link_t *link)
     tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
 
     link->io.NumPorts1 = 16;
-    i = first_tuple(link->handle, tuple, parse);
+    i = first_tuple(link, tuple, parse);
     while (i != CS_NO_MORE_ITEMS) {
        if (i == CS_SUCCESS) {
            link->conf.ConfigIndex = cf->index;
            link->io.BasePort1 = cf->io.win[0].base;
            link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
-           i = pcmcia_request_io(link->handle, &link->io);
+           i = pcmcia_request_io(link, &link->io);
            if (i == CS_SUCCESS) break;
        }
-       i = next_tuple(link->handle, tuple, parse);
+       i = next_tuple(link, tuple, parse);
     }
     if (i == CS_SUCCESS)
        dev->base_addr = link->io.BasePort1;
@@ -714,9 +704,8 @@ static int smc_config(dev_link_t *link)
     return i;
 }
 
-static int smc_setup(dev_link_t *link)
+static int smc_setup(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     struct net_device *dev = link->priv;
     struct smc_cfg_mem *cfg_mem;
     tuple_t *tuple;
@@ -739,11 +728,11 @@ static int smc_setup(dev_link_t *link)
 
     /* Check for a LAN function extension tuple */
     tuple->DesiredTuple = CISTPL_FUNCE;
-    i = first_tuple(handle, tuple, parse);
+    i = first_tuple(link, tuple, parse);
     while (i == CS_SUCCESS) {
        if (parse->funce.type == CISTPL_FUNCE_LAN_NODE_ID)
            break;
-       i = next_tuple(handle, tuple, parse);
+       i = next_tuple(link, tuple, parse);
     }
     if (i == CS_SUCCESS) {
        node_id = (cistpl_lan_node_id_t *)parse->funce.data;
@@ -756,7 +745,7 @@ static int smc_setup(dev_link_t *link)
     }
     /* Try the third string in the Version 1 Version/ID tuple. */
     tuple->DesiredTuple = CISTPL_VERS_1;
-    if (first_tuple(handle, tuple, parse) != CS_SUCCESS) {
+    if (first_tuple(link, tuple, parse) != CS_SUCCESS) {
        rc = -1;
        goto free_cfg_mem;
     }
@@ -774,7 +763,7 @@ free_cfg_mem:
 
 /*====================================================================*/
 
-static int osi_config(dev_link_t *link)
+static int osi_config(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     static const kio_addr_t com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
@@ -794,22 +783,21 @@ static int osi_config(dev_link_t *link)
 
     for (i = j = 0; j < 4; j++) {
        link->io.BasePort2 = com[j];
-       i = pcmcia_request_io(link->handle, &link->io);
+       i = pcmcia_request_io(link, &link->io);
        if (i == CS_SUCCESS) break;
     }
     if (i != CS_SUCCESS) {
        /* Fallback: turn off hard decode */
        link->conf.ConfigIndex = 0x03;
        link->io.NumPorts2 = 0;
-       i = pcmcia_request_io(link->handle, &link->io);
+       i = pcmcia_request_io(link, &link->io);
     }
     dev->base_addr = link->io.BasePort1 + 0x10;
     return i;
 }
 
-static int osi_setup(dev_link_t *link, u_short manfid, u_short cardid)
+static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid)
 {
-    client_handle_t handle = link->handle;
     struct net_device *dev = link->priv;
     struct smc_cfg_mem *cfg_mem;
     tuple_t *tuple;
@@ -830,12 +818,12 @@ static int osi_setup(dev_link_t *link, u_short manfid, u_short cardid)
 
     /* Read the station address from tuple 0x90, subtuple 0x04 */
     tuple->DesiredTuple = 0x90;
-    i = pcmcia_get_first_tuple(handle, tuple);
+    i = pcmcia_get_first_tuple(link, tuple);
     while (i == CS_SUCCESS) {
-       i = pcmcia_get_tuple_data(handle, tuple);
+       i = pcmcia_get_tuple_data(link, tuple);
        if ((i != CS_SUCCESS) || (buf[0] == 0x04))
            break;
-       i = pcmcia_get_next_tuple(handle, tuple);
+       i = pcmcia_get_next_tuple(link, tuple);
     }
     if (i != CS_SUCCESS) {
        rc = -1;
@@ -868,57 +856,47 @@ free_cfg_mem:
    return rc;
 }
 
-static int smc91c92_suspend(struct pcmcia_device *p_dev)
+static int smc91c92_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-       }
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int smc91c92_resume(struct pcmcia_device *p_dev)
+static int smc91c92_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
        struct smc_private *smc = netdev_priv(dev);
        int i;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               if ((smc->manfid == MANFID_MEGAHERTZ) &&
-                   (smc->cardid == PRODID_MEGAHERTZ_EM3288))
-                       mhz_3288_power(link);
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (smc->manfid == MANFID_MOTOROLA)
-                       mot_config(link);
-               if ((smc->manfid == MANFID_OSITECH) &&
-                   (smc->cardid != PRODID_OSITECH_SEVEN)) {
-                       /* Power up the card and enable interrupts */
-                       set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
-                       set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
-               }
-               if (((smc->manfid == MANFID_OSITECH) &&
-                    (smc->cardid == PRODID_OSITECH_SEVEN)) ||
-                   ((smc->manfid == MANFID_PSION) &&
-                    (smc->cardid == PRODID_PSION_NET100))) {
-                       /* Download the Seven of Diamonds firmware */
-                       for (i = 0; i < sizeof(__Xilinx7OD); i++) {
-                               outb(__Xilinx7OD[i], link->io.BasePort1+2);
-                               udelay(50);
-                       }
-               }
-               if (link->open) {
-                       smc_reset(dev);
-                       netif_device_attach(dev);
+       if ((smc->manfid == MANFID_MEGAHERTZ) &&
+           (smc->cardid == PRODID_MEGAHERTZ_EM3288))
+               mhz_3288_power(link);
+       if (smc->manfid == MANFID_MOTOROLA)
+               mot_config(link);
+       if ((smc->manfid == MANFID_OSITECH) &&
+           (smc->cardid != PRODID_OSITECH_SEVEN)) {
+               /* Power up the card and enable interrupts */
+               set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
+               set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
+       }
+       if (((smc->manfid == MANFID_OSITECH) &&
+            (smc->cardid == PRODID_OSITECH_SEVEN)) ||
+           ((smc->manfid == MANFID_PSION) &&
+            (smc->cardid == PRODID_PSION_NET100))) {
+               /* Download the Seven of Diamonds firmware */
+               for (i = 0; i < sizeof(__Xilinx7OD); i++) {
+                       outb(__Xilinx7OD[i], link->io.BasePort1+2);
+                       udelay(50);
                }
        }
+       if (link->open) {
+               smc_reset(dev);
+               netif_device_attach(dev);
+       }
 
        return 0;
 }
@@ -931,7 +909,7 @@ static int smc91c92_resume(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static int check_sig(dev_link_t *link)
+static int check_sig(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv;
     kio_addr_t ioaddr = dev->base_addr;
@@ -964,13 +942,15 @@ static int check_sig(dev_link_t *link)
     }
 
     if (width) {
-       printk(KERN_INFO "smc91c92_cs: using 8-bit IO window.\n");
-       smc91c92_suspend(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-       pcmcia_request_io(link->handle, &link->io);
-       smc91c92_resume(link->handle);
-       return check_sig(link);
+           modconf_t mod = {
+                   .Attributes = CONF_IO_CHANGE_WIDTH,
+           };
+           printk(KERN_INFO "smc91c92_cs: using 8-bit IO window.\n");
+
+           smc91c92_suspend(link);
+           pcmcia_modify_configuration(link, &mod);
+           smc91c92_resume(link);
+           return check_sig(link);
     }
     return -ENODEV;
 }
@@ -984,11 +964,10 @@ static int check_sig(dev_link_t *link)
 ======================================================================*/
 
 #define CS_EXIT_TEST(ret, svc, label) \
-if (ret != CS_SUCCESS) { cs_error(link->handle, svc, ret); goto label; }
+if (ret != CS_SUCCESS) { cs_error(link, svc, ret); goto label; }
 
-static void smc91c92_config(dev_link_t *link)
+static int smc91c92_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     struct net_device *dev = link->priv;
     struct smc_private *smc = netdev_priv(dev);
     struct smc_cfg_mem *cfg_mem;
@@ -1015,21 +994,18 @@ static void smc91c92_config(dev_link_t *link)
     tuple->TupleDataMax = 64;
 
     tuple->DesiredTuple = CISTPL_CONFIG;
-    i = first_tuple(handle, tuple, parse);
+    i = first_tuple(link, tuple, parse);
     CS_EXIT_TEST(i, ParseTuple, config_failed);
     link->conf.ConfigBase = parse->config.base;
     link->conf.Present = parse->config.rmask[0];
 
     tuple->DesiredTuple = CISTPL_MANFID;
     tuple->Attributes = TUPLE_RETURN_COMMON;
-    if (first_tuple(handle, tuple, parse) == CS_SUCCESS) {
+    if (first_tuple(link, tuple, parse) == CS_SUCCESS) {
        smc->manfid = parse->manfid.manf;
        smc->cardid = parse->manfid.card;
     }
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
     if ((smc->manfid == MANFID_OSITECH) &&
        (smc->cardid != PRODID_OSITECH_SEVEN)) {
        i = osi_config(link);
@@ -1043,9 +1019,9 @@ static void smc91c92_config(dev_link_t *link)
     }
     CS_EXIT_TEST(i, RequestIO, config_failed);
 
-    i = pcmcia_request_irq(link->handle, &link->irq);
+    i = pcmcia_request_irq(link, &link->irq);
     CS_EXIT_TEST(i, RequestIRQ, config_failed);
-    i = pcmcia_request_configuration(link->handle, &link->conf);
+    i = pcmcia_request_configuration(link, &link->conf);
     CS_EXIT_TEST(i, RequestConfiguration, config_failed);
 
     if (smc->manfid == MANFID_MOTOROLA)
@@ -1124,13 +1100,12 @@ static void smc91c92_config(dev_link_t *link)
        SMC_SELECT_BANK(0);
     }
 
-    link->dev = &smc->node;
-    link->state &= ~DEV_CONFIG_PENDING;
-    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+    link->dev_node = &smc->node;
+    SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
     if (register_netdev(dev) != 0) {
        printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n");
-       link->dev = NULL;
+       link->dev_node = NULL;
        goto config_undo;
     }
 
@@ -1160,15 +1135,14 @@ static void smc91c92_config(dev_link_t *link)
        }
     }
     kfree(cfg_mem);
-    return;
+    return 0;
 
 config_undo:
     unregister_netdev(dev);
 config_failed:                 /* CS_EXIT_TEST() calls jump to here... */
     smc91c92_release(link);
-    link->state &= ~DEV_CONFIG_PENDING;
     kfree(cfg_mem);
-
+    return -ENODEV;
 } /* smc91c92_config */
 
 /*======================================================================
@@ -1179,22 +1153,15 @@ config_failed:                  /* CS_EXIT_TEST() calls jump to here... */
 
 ======================================================================*/
 
-static void smc91c92_release(dev_link_t *link)
+static void smc91c92_release(struct pcmcia_device *link)
 {
-
-    DEBUG(0, "smc91c92_release(0x%p)\n", link);
-
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    if (link->win) {
-       struct net_device *dev = link->priv;
-       struct smc_private *smc = netdev_priv(dev);
-       iounmap(smc->base);
-       pcmcia_release_window(link->win);
-    }
-
-    link->state &= ~DEV_CONFIG;
+       DEBUG(0, "smc91c92_release(0x%p)\n", link);
+       if (link->win) {
+               struct net_device *dev = link->priv;
+               struct smc_private *smc = netdev_priv(dev);
+               iounmap(smc->base);
+       }
+       pcmcia_disable_device(link);
 }
 
 /*======================================================================
@@ -1283,7 +1250,7 @@ static void smc_dump(struct net_device *dev)
 static int smc_open(struct net_device *dev)
 {
     struct smc_private *smc = netdev_priv(dev);
-    dev_link_t *link = &smc->link;
+    struct pcmcia_device *link = smc->p_dev;
 
 #ifdef PCMCIA_DEBUG
     DEBUG(0, "%s: smc_open(%p), ID/Window %4.4x.\n",
@@ -1292,7 +1259,7 @@ static int smc_open(struct net_device *dev)
 #endif
 
     /* Check that the PCMCIA card is still here. */
-    if (!DEV_OK(link))
+    if (!pcmcia_dev_present(link))
        return -ENODEV;
     /* Physical device present signature. */
     if (check_sig(link) < 0) {
@@ -1320,7 +1287,7 @@ static int smc_open(struct net_device *dev)
 static int smc_close(struct net_device *dev)
 {
     struct smc_private *smc = netdev_priv(dev);
-    dev_link_t *link = &smc->link;
+    struct pcmcia_device *link = smc->p_dev;
     kio_addr_t ioaddr = dev->base_addr;
 
     DEBUG(0, "%s: smc_close(), status %4.4x.\n",
@@ -2311,7 +2278,7 @@ static struct pcmcia_driver smc91c92_cs_driver = {
        .drv            = {
                .name   = "smc91c92_cs",
        },
-       .probe          = smc91c92_attach,
+       .probe          = smc91c92_probe,
        .remove         = smc91c92_detach,
        .id_table       = smc91c92_ids,
        .suspend        = smc91c92_suspend,
index e8f849e..71f4505 100644 (file)
@@ -289,9 +289,9 @@ static void mii_wr(kio_addr_t ioaddr, u_char phyaddr, u_char phyreg,
  * and ejection events.  They are invoked from the event handler.
  */
 
-static int has_ce2_string(dev_link_t * link);
-static void xirc2ps_config(dev_link_t * link);
-static void xirc2ps_release(dev_link_t * link);
+static int has_ce2_string(struct pcmcia_device * link);
+static int xirc2ps_config(struct pcmcia_device * link);
+static void xirc2ps_release(struct pcmcia_device * link);
 
 /****************
  * The attach() and detach() entry points are used to create and destroy
@@ -313,10 +313,10 @@ static irqreturn_t xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs
 /****************
  * A linked list of "instances" of the device.  Each actual
  * PCMCIA card corresponds to one device instance, and is described
- * by one dev_link_t structure (defined in ds.h).
+ * by one struct pcmcia_device structure (defined in ds.h).
  *
  * You may not want to use a linked list for this -- for example, the
- * memory card driver uses an array of dev_link_t pointers, where minor
+ * memory card driver uses an array of struct pcmcia_device pointers, where minor
  * device numbers are used to derive the corresponding array index.
  */
 
@@ -326,13 +326,13 @@ static irqreturn_t xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs
  * example, ethernet cards, modems).  In other cases, there may be
  * many actual or logical devices (SCSI adapters, memory cards with
  * multiple partitions).  The dev_node_t structures need to be kept
- * in a linked list starting at the 'dev' field of a dev_link_t
+ * in a linked list starting at the 'dev' field of a struct pcmcia_device
  * structure.  We allocate them in the card's private data structure,
  * because they generally can't be allocated dynamically.
  */
 
 typedef struct local_info_t {
-    dev_link_t link;
+       struct pcmcia_device    *p_dev;
     dev_node_t node;
     struct net_device_stats stats;
     int card_type;
@@ -355,7 +355,7 @@ static void do_tx_timeout(struct net_device *dev);
 static struct net_device_stats *do_get_stats(struct net_device *dev);
 static void set_addresses(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
-static int set_card_type(dev_link_t *link, const void *s);
+static int set_card_type(struct pcmcia_device *link, const void *s);
 static int do_config(struct net_device *dev, struct ifmap *map);
 static int do_open(struct net_device *dev);
 static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -368,7 +368,7 @@ static int do_stop(struct net_device *dev);
 
 /*=============== Helper functions =========================*/
 static int
-first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        int err;
 
@@ -379,7 +379,7 @@ first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
 }
 
 static int
-next_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
 {
        int err;
 
@@ -553,9 +553,8 @@ mii_wr(kio_addr_t ioaddr, u_char phyaddr, u_char phyreg, unsigned data, int len)
  */
 
 static int
-xirc2ps_attach(struct pcmcia_device *p_dev)
+xirc2ps_probe(struct pcmcia_device *link)
 {
-    dev_link_t *link;
     struct net_device *dev;
     local_info_t *local;
 
@@ -566,12 +565,11 @@ xirc2ps_attach(struct pcmcia_device *p_dev)
     if (!dev)
            return -ENOMEM;
     local = netdev_priv(dev);
-    link = &local->link;
+    local->p_dev = link;
     link->priv = dev;
 
     /* General socket configuration */
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
     link->conf.ConfigIndex = 1;
     link->conf.Present = PRESENT_OPTION;
@@ -593,13 +591,7 @@ xirc2ps_attach(struct pcmcia_device *p_dev)
     dev->watchdog_timeo = TX_TIMEOUT;
 #endif
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    xirc2ps_config(link);
-
-    return 0;
+    return xirc2ps_config(link);
 } /* xirc2ps_attach */
 
 /****************
@@ -610,18 +602,16 @@ xirc2ps_attach(struct pcmcia_device *p_dev)
  */
 
 static void
-xirc2ps_detach(struct pcmcia_device *p_dev)
+xirc2ps_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
     struct net_device *dev = link->priv;
 
     DEBUG(0, "detach(0x%p)\n", link);
 
-    if (link->dev)
+    if (link->dev_node)
        unregister_netdev(dev);
 
-    if (link->state & DEV_CONFIG)
-       xirc2ps_release(link);
+    xirc2ps_release(link);
 
     free_netdev(dev);
 } /* xirc2ps_detach */
@@ -645,7 +635,7 @@ xirc2ps_detach(struct pcmcia_device *p_dev)
  *
  */
 static int
-set_card_type(dev_link_t *link, const void *s)
+set_card_type(struct pcmcia_device *link, const void *s)
 {
     struct net_device *dev = link->priv;
     local_info_t *local = netdev_priv(dev);
@@ -714,9 +704,8 @@ set_card_type(dev_link_t *link, const void *s)
  * Returns: true if this is a CE2
  */
 static int
-has_ce2_string(dev_link_t * link)
+has_ce2_string(struct pcmcia_device * link)
 {
-    client_handle_t handle = link->handle;
     tuple_t tuple;
     cisparse_t parse;
     u_char buf[256];
@@ -726,7 +715,7 @@ has_ce2_string(dev_link_t * link)
     tuple.TupleDataMax = 254;
     tuple.TupleOffset = 0;
     tuple.DesiredTuple = CISTPL_VERS_1;
-    if (!first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 2) {
+    if (!first_tuple(link, &tuple, &parse) && parse.version_1.ns > 2) {
        if (strstr(parse.version_1.str + parse.version_1.ofs[2], "CE2"))
            return 1;
     }
@@ -738,10 +727,9 @@ has_ce2_string(dev_link_t * link)
  * is received, to configure the PCMCIA socket, and to make the
  * ethernet device available to the system.
  */
-static void
-xirc2ps_config(dev_link_t * link)
+static int
+xirc2ps_config(struct pcmcia_device * link)
 {
-    client_handle_t handle = link->handle;
     struct net_device *dev = link->priv;
     local_info_t *local = netdev_priv(dev);
     tuple_t tuple;
@@ -767,7 +755,7 @@ xirc2ps_config(dev_link_t * link)
 
     /* Is this a valid card */
     tuple.DesiredTuple = CISTPL_MANFID;
-    if ((err=first_tuple(handle, &tuple, &parse))) {
+    if ((err=first_tuple(link, &tuple, &parse))) {
        printk(KNOT_XIRC "manfid not found in CIS\n");
        goto failure;
     }
@@ -803,15 +791,15 @@ xirc2ps_config(dev_link_t * link)
 
     /* get configuration stuff */
     tuple.DesiredTuple = CISTPL_CONFIG;
-    if ((err=first_tuple(handle, &tuple, &parse)))
+    if ((err=first_tuple(link, &tuple, &parse)))
        goto cis_error;
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present =    parse.config.rmask[0];
 
     /* get the ethernet address from the CIS */
     tuple.DesiredTuple = CISTPL_FUNCE;
-    for (err = first_tuple(handle, &tuple, &parse); !err;
-                            err = next_tuple(handle, &tuple, &parse)) {
+    for (err = first_tuple(link, &tuple, &parse); !err;
+                            err = next_tuple(link, &tuple, &parse)) {
        /* Once I saw two CISTPL_FUNCE_LAN_NODE_ID entries:
         * the first one with a length of zero the second correct -
         * so I skip all entries with length 0 */
@@ -821,8 +809,8 @@ xirc2ps_config(dev_link_t * link)
     }
     if (err) { /* not found: try to get the node-id from tuple 0x89 */
        tuple.DesiredTuple = 0x89;  /* data layout looks like tuple 0x22 */
-       if ((err = pcmcia_get_first_tuple(handle, &tuple)) == 0 &&
-               (err = pcmcia_get_tuple_data(handle, &tuple)) == 0) {
+       if ((err = pcmcia_get_first_tuple(link, &tuple)) == 0 &&
+               (err = pcmcia_get_tuple_data(link, &tuple)) == 0) {
            if (tuple.TupleDataLen == 8 && *buf == CISTPL_FUNCE_LAN_NODE_ID)
                memcpy(&parse, buf, 8);
            else
@@ -831,8 +819,8 @@ xirc2ps_config(dev_link_t * link)
     }
     if (err) { /* another try  (James Lehmer's CE2 version 4.1)*/
        tuple.DesiredTuple = CISTPL_FUNCE;
-       for (err = first_tuple(handle, &tuple, &parse); !err;
-                                err = next_tuple(handle, &tuple, &parse)) {
+       for (err = first_tuple(link, &tuple, &parse); !err;
+                                err = next_tuple(link, &tuple, &parse)) {
            if (parse.funce.type == 0x02 && parse.funce.data[0] == 1
                && parse.funce.data[1] == 6 && tuple.TupleDataLen == 13) {
                buf[1] = 4;
@@ -853,9 +841,6 @@ xirc2ps_config(dev_link_t * link)
     for (i=0; i < 6; i++)
        dev->dev_addr[i] = node_id->id[i];
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
     link->io.IOAddrLines =10;
     link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
     link->irq.Attributes = IRQ_HANDLE_PRESENT;
@@ -875,14 +860,14 @@ xirc2ps_config(dev_link_t * link)
             * Ethernet port */
            link->io.NumPorts1 = 16; /* no Mako stuff anymore */
            tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-           for (err = first_tuple(handle, &tuple, &parse); !err;
-                                err = next_tuple(handle, &tuple, &parse)) {
+           for (err = first_tuple(link, &tuple, &parse); !err;
+                                err = next_tuple(link, &tuple, &parse)) {
                if (cf->io.nwin > 0  &&  (cf->io.win[0].base & 0xf) == 8) {
                    for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
                        link->conf.ConfigIndex = cf->index ;
                        link->io.BasePort2 = cf->io.win[0].base;
                        link->io.BasePort1 = ioaddr;
-                       if (!(err=pcmcia_request_io(link->handle, &link->io)))
+                       if (!(err=pcmcia_request_io(link, &link->io)))
                            goto port_found;
                    }
                }
@@ -896,15 +881,15 @@ xirc2ps_config(dev_link_t * link)
             */
            for (pass=0; pass < 2; pass++) {
                tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-               for (err = first_tuple(handle, &tuple, &parse); !err;
-                                    err = next_tuple(handle, &tuple, &parse)){
+               for (err = first_tuple(link, &tuple, &parse); !err;
+                                    err = next_tuple(link, &tuple, &parse)){
                    if (cf->io.nwin > 0  &&  (cf->io.win[0].base & 0xf) == 8){
                        link->conf.ConfigIndex = cf->index ;
                        link->io.BasePort2 = cf->io.win[0].base;
                        link->io.BasePort1 = link->io.BasePort2
                                    + (pass ? (cf->index & 0x20 ? -24:8)
                                            : (cf->index & 0x20 ?   8:-24));
-                       if (!(err=pcmcia_request_io(link->handle, &link->io)))
+                       if (!(err=pcmcia_request_io(link, &link->io)))
                            goto port_found;
                    }
                }
@@ -919,12 +904,12 @@ xirc2ps_config(dev_link_t * link)
        link->io.NumPorts1 = 16;
        for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
            link->io.BasePort1 = ioaddr;
-           if (!(err=pcmcia_request_io(link->handle, &link->io)))
+           if (!(err=pcmcia_request_io(link, &link->io)))
                goto port_found;
        }
        link->io.BasePort1 = 0; /* let CS decide */
-       if ((err=pcmcia_request_io(link->handle, &link->io))) {
-           cs_error(link->handle, RequestIO, err);
+       if ((err=pcmcia_request_io(link, &link->io))) {
+           cs_error(link, RequestIO, err);
            goto config_error;
        }
     }
@@ -936,8 +921,8 @@ xirc2ps_config(dev_link_t * link)
      * Now allocate an interrupt line. Note that this does not
      * actually assign a handler to the interrupt.
      */
-    if ((err=pcmcia_request_irq(link->handle, &link->irq))) {
-       cs_error(link->handle, RequestIRQ, err);
+    if ((err=pcmcia_request_irq(link, &link->irq))) {
+       cs_error(link, RequestIRQ, err);
        goto config_error;
     }
 
@@ -945,8 +930,8 @@ xirc2ps_config(dev_link_t * link)
      * This actually configures the PCMCIA socket -- setting up
      * the I/O windows and the interrupt mapping.
      */
-    if ((err=pcmcia_request_configuration(link->handle, &link->conf))) {
-       cs_error(link->handle, RequestConfiguration, err);
+    if ((err=pcmcia_request_configuration(link, &link->conf))) {
+       cs_error(link, RequestConfiguration, err);
        goto config_error;
     }
 
@@ -963,15 +948,15 @@ xirc2ps_config(dev_link_t * link)
        reg.Action = CS_WRITE;
        reg.Offset = CISREG_IOBASE_0;
        reg.Value = link->io.BasePort2 & 0xff;
-       if ((err = pcmcia_access_configuration_register(link->handle, &reg))) {
-           cs_error(link->handle, AccessConfigurationRegister, err);
+       if ((err = pcmcia_access_configuration_register(link, &reg))) {
+           cs_error(link, AccessConfigurationRegister, err);
            goto config_error;
        }
        reg.Action = CS_WRITE;
        reg.Offset = CISREG_IOBASE_1;
        reg.Value = (link->io.BasePort2 >> 8) & 0xff;
-       if ((err = pcmcia_access_configuration_register(link->handle, &reg))) {
-           cs_error(link->handle, AccessConfigurationRegister, err);
+       if ((err = pcmcia_access_configuration_register(link, &reg))) {
+           cs_error(link, AccessConfigurationRegister, err);
            goto config_error;
        }
 
@@ -982,15 +967,15 @@ xirc2ps_config(dev_link_t * link)
        req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
        req.Base = req.Size = 0;
        req.AccessSpeed = 0;
-       if ((err = pcmcia_request_window(&link->handle, &req, &link->win))) {
-           cs_error(link->handle, RequestWindow, err);
+       if ((err = pcmcia_request_window(&link, &req, &link->win))) {
+           cs_error(link, RequestWindow, err);
            goto config_error;
        }
        local->dingo_ccr = ioremap(req.Base,0x1000) + 0x0800;
        mem.CardOffset = 0x0;
        mem.Page = 0;
        if ((err = pcmcia_map_mem_page(link->win, &mem))) {
-           cs_error(link->handle, MapMemPage, err);
+           cs_error(link, MapMemPage, err);
            goto config_error;
        }
 
@@ -1050,13 +1035,12 @@ xirc2ps_config(dev_link_t * link)
     if (local->dingo)
        do_reset(dev, 1); /* a kludge to make the cem56 work */
 
-    link->dev = &local->node;
-    link->state &= ~DEV_CONFIG_PENDING;
-    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+    link->dev_node = &local->node;
+    SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
     if ((err=register_netdev(dev))) {
        printk(KNOT_XIRC "register_netdev() failed\n");
-       link->dev = NULL;
+       link->dev_node = NULL;
        goto config_error;
     }
 
@@ -1069,17 +1053,16 @@ xirc2ps_config(dev_link_t * link)
        printk("%c%02X", i?':':' ', dev->dev_addr[i]);
     printk("\n");
 
-    return;
+    return 0;
 
   config_error:
-    link->state &= ~DEV_CONFIG_PENDING;
     xirc2ps_release(link);
-    return;
+    return -ENODEV;
 
   cis_error:
     printk(KNOT_XIRC "unable to parse CIS\n");
   failure:
-    link->state &= ~DEV_CONFIG_PENDING;
+    return -ENODEV;
 } /* xirc2ps_config */
 
 /****************
@@ -1088,57 +1071,41 @@ xirc2ps_config(dev_link_t * link)
  * still open, this will be postponed until it is closed.
  */
 static void
-xirc2ps_release(dev_link_t *link)
+xirc2ps_release(struct pcmcia_device *link)
 {
+       DEBUG(0, "release(0x%p)\n", link);
 
-    DEBUG(0, "release(0x%p)\n", link);
-
-    if (link->win) {
-       struct net_device *dev = link->priv;
-       local_info_t *local = netdev_priv(dev);
-       if (local->dingo)
-           iounmap(local->dingo_ccr - 0x0800);
-       pcmcia_release_window(link->win);
-    }
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    link->state &= ~DEV_CONFIG;
-
+       if (link->win) {
+               struct net_device *dev = link->priv;
+               local_info_t *local = netdev_priv(dev);
+               if (local->dingo)
+                       iounmap(local->dingo_ccr - 0x0800);
+       }
+       pcmcia_disable_device(link);
 } /* xirc2ps_release */
 
 /*====================================================================*/
 
 
-static int xirc2ps_suspend(struct pcmcia_device *p_dev)
+static int xirc2ps_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               if (link->open) {
-                       netif_device_detach(dev);
-                       do_powerdown(dev);
-               }
-               pcmcia_release_configuration(link->handle);
+       if (link->open) {
+               netif_device_detach(dev);
+               do_powerdown(dev);
        }
 
        return 0;
 }
 
-static int xirc2ps_resume(struct pcmcia_device *p_dev)
+static int xirc2ps_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       do_reset(dev,1);
-                       netif_device_attach(dev);
-               }
+       if (link->open) {
+               do_reset(dev,1);
+               netif_device_attach(dev);
        }
 
        return 0;
@@ -1552,13 +1519,13 @@ static int
 do_open(struct net_device *dev)
 {
     local_info_t *lp = netdev_priv(dev);
-    dev_link_t *link = &lp->link;
+    struct pcmcia_device *link = lp->p_dev;
 
     DEBUG(0, "do_open(%p)\n", dev);
 
     /* Check that the PCMCIA card is still here. */
     /* Physical device present signature. */
-    if (!DEV_OK(link))
+    if (!pcmcia_dev_present(link))
        return -ENODEV;
 
     /* okay */
@@ -1882,7 +1849,7 @@ do_stop(struct net_device *dev)
 {
     kio_addr_t ioaddr = dev->base_addr;
     local_info_t *lp = netdev_priv(dev);
-    dev_link_t *link = &lp->link;
+    struct pcmcia_device *link = lp->p_dev;
 
     DEBUG(0, "do_stop(%p)\n", dev);
 
@@ -1935,7 +1902,7 @@ static struct pcmcia_driver xirc2ps_cs_driver = {
        .drv            = {
                .name   = "xirc2ps_cs",
        },
-       .probe          = xirc2ps_attach,
+       .probe          = xirc2ps_probe,
        .remove         = xirc2ps_detach,
        .id_table       = xirc2ps_ids,
        .suspend        = xirc2ps_suspend,
index 35b1805..45ad036 100644 (file)
@@ -2122,8 +2122,7 @@ static void __devexit starfire_remove_one (struct pci_dev *pdev)
        struct net_device *dev = pci_get_drvdata(pdev);
        struct netdev_private *np = netdev_priv(dev);
 
-       if (!dev)
-               BUG();
+       BUG_ON(!dev);
 
        unregister_netdev(dev);
 
index 964c096..0b53580 100644 (file)
@@ -69,8 +69,8 @@
 
 #define DRV_MODULE_NAME                "tg3"
 #define PFX DRV_MODULE_NAME    ": "
-#define DRV_MODULE_VERSION     "3.55"
-#define DRV_MODULE_RELDATE     "Mar 27, 2006"
+#define DRV_MODULE_VERSION     "3.56"
+#define DRV_MODULE_RELDATE     "Apr 1, 2006"
 
 #define TG3_DEF_MAC_MODE       0
 #define TG3_DEF_RX_MODE                0
@@ -497,40 +497,33 @@ static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val)
        unsigned long flags;
 
        spin_lock_irqsave(&tp->indirect_lock, flags);
-       if (tp->write32 != tg3_write_indirect_reg32) {
-               tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off);
-               tw32_f(TG3PCI_MEM_WIN_DATA, val);
+       pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
+       pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
 
-               /* Always leave this as zero. */
-               tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0);
-       } else {
-               pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
-               pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
-
-               /* Always leave this as zero. */
-               pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
-       }
+       /* Always leave this as zero. */
+       pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
        spin_unlock_irqrestore(&tp->indirect_lock, flags);
 }
 
+static void tg3_write_mem_fast(struct tg3 *tp, u32 off, u32 val)
+{
+       /* If no workaround is needed, write to mem space directly */
+       if (tp->write32 != tg3_write_indirect_reg32)
+               tw32(NIC_SRAM_WIN_BASE + off, val);
+       else
+               tg3_write_mem(tp, off, val);
+}
+
 static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&tp->indirect_lock, flags);
-       if (tp->write32 != tg3_write_indirect_reg32) {
-               tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off);
-               *val = tr32(TG3PCI_MEM_WIN_DATA);
+       pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
+       pci_read_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
 
-               /* Always leave this as zero. */
-               tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0);
-       } else {
-               pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
-               pci_read_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
-
-               /* Always leave this as zero. */
-               pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
-       }
+       /* Always leave this as zero. */
+       pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
        spin_unlock_irqrestore(&tp->indirect_lock, flags);
 }
 
@@ -1374,12 +1367,12 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
                }
        }
 
-       tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN);
-
        /* Finally, set the new power state. */
        pci_write_config_word(tp->pdev, pm + PCI_PM_CTRL, power_control);
        udelay(100);    /* Delay after power state change */
 
+       tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN);
+
        return 0;
 }
 
@@ -2966,9 +2959,7 @@ static void tg3_tx(struct tg3 *tp)
                struct sk_buff *skb = ri->skb;
                int i;
 
-               if (unlikely(skb == NULL))
-                       BUG();
-
+               BUG_ON(skb == NULL);
                pci_unmap_single(tp->pdev,
                                 pci_unmap_addr(ri, mapping),
                                 skb_headlen(skb),
@@ -2979,12 +2970,10 @@ static void tg3_tx(struct tg3 *tp)
                sw_idx = NEXT_TX(sw_idx);
 
                for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-                       if (unlikely(sw_idx == hw_idx))
-                               BUG();
+                       BUG_ON(sw_idx == hw_idx);
 
                        ri = &tp->tx_buffers[sw_idx];
-                       if (unlikely(ri->skb != NULL))
-                               BUG();
+                       BUG_ON(ri->skb != NULL);
 
                        pci_unmap_page(tp->pdev,
                                       pci_unmap_addr(ri, mapping),
@@ -4935,9 +4924,8 @@ static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
 {
        int i;
 
-       if (offset == TX_CPU_BASE &&
-           (tp->tg3_flags2 & TG3_FLG2_5705_PLUS))
-               BUG();
+       BUG_ON(offset == TX_CPU_BASE &&
+           (tp->tg3_flags2 & TG3_FLG2_5705_PLUS));
 
        if (offset == RX_CPU_BASE) {
                for (i = 0; i < 10000; i++) {
@@ -6547,11 +6535,11 @@ static void tg3_timer(unsigned long __opaque)
                if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) {
                        u32 val;
 
-                       tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX,
-                                     FWCMD_NICDRV_ALIVE2);
-                       tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4);
+                       tg3_write_mem_fast(tp, NIC_SRAM_FW_CMD_MBOX,
+                                          FWCMD_NICDRV_ALIVE2);
+                       tg3_write_mem_fast(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4);
                        /* 5 seconds timeout */
-                       tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, 5);
+                       tg3_write_mem_fast(tp, NIC_SRAM_FW_CMD_DATA_MBOX, 5);
                        val = tr32(GRC_RX_CPU_EVENT);
                        val |= (1 << 14);
                        tw32(GRC_RX_CPU_EVENT, val);
index 9345e68..649d8ea 100644 (file)
@@ -438,8 +438,7 @@ static void __devexit abyss_detach (struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
        
-       if (!dev)
-               BUG();
+       BUG_ON(!dev);
        unregister_netdev(dev);
        release_region(dev->base_addr-0x10, ABYSS_IO_EXTENT);
        free_irq(dev->irq, dev);
index 3a25d19..19e6f4d 100644 (file)
@@ -735,8 +735,7 @@ static int __devexit madgemc_remove(struct device *device)
        struct net_local *tp;
         struct card_info *card;
 
-       if (!dev)
-               BUG();
+       BUG_ON(!dev);
 
        tp = dev->priv;
        card = tp->tmspriv;
index a496460..af0cbb6 100644 (file)
@@ -80,8 +80,8 @@ MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340 PCMCIA cards");
    event handler. 
 */
 
-static void airo_config(dev_link_t *link);
-static void airo_release(dev_link_t *link);
+static int airo_config(struct pcmcia_device *link);
+static void airo_release(struct pcmcia_device *link);
 
 /*
    The attach() and detach() entry points are used to create and destroy
@@ -101,10 +101,10 @@ static void airo_detach(struct pcmcia_device *p_dev);
 /*
    A linked list of "instances" of the  aironet device.  Each actual
    PCMCIA card corresponds to one device instance, and is described
-   by one dev_link_t structure (defined in ds.h).
+   by one struct pcmcia_device structure (defined in ds.h).
 
    You may not want to use a linked list for this -- for example, the
-   memory card driver uses an array of dev_link_t pointers, where minor
+   memory card driver uses an array of struct pcmcia_device pointers, where minor
    device numbers are used to derive the corresponding array index.
 */
 
@@ -114,7 +114,7 @@ static void airo_detach(struct pcmcia_device *p_dev);
    example, ethernet cards, modems).  In other cases, there may be
    many actual or logical devices (SCSI adapters, memory cards with
    multiple partitions).  The dev_node_t structures need to be kept
-   in a linked list starting at the 'dev' field of a dev_link_t
+   in a linked list starting at the 'dev' field of a struct pcmcia_device
    structure.  We allocate them in the card's private data structure,
    because they generally shouldn't be allocated dynamically.
 
@@ -141,24 +141,16 @@ typedef struct local_info_t {
   
   ======================================================================*/
 
-static int airo_attach(struct pcmcia_device *p_dev)
+static int airo_probe(struct pcmcia_device *p_dev)
 {
-       dev_link_t *link;
        local_info_t *local;
 
        DEBUG(0, "airo_attach()\n");
 
-       /* Initialize the dev_link_t structure */
-       link = kzalloc(sizeof(struct dev_link_t), GFP_KERNEL);
-       if (!link) {
-               printk(KERN_ERR "airo_cs: no memory for new device\n");
-               return -ENOMEM;
-       }
-       
        /* Interrupt setup */
-       link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
-       link->irq.IRQInfo1 = IRQ_LEVEL_ID;
-       link->irq.Handler = NULL;
+       p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+       p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+       p_dev->irq.Handler = NULL;
        
        /*
          General socket configuration defaults can go here.  In this
@@ -167,26 +159,18 @@ static int airo_attach(struct pcmcia_device *p_dev)
          and attributes of IO windows) are fixed by the nature of the
          device, and can be hard-wired here.
        */
-       link->conf.Attributes = 0;
-       link->conf.Vcc = 50;
-       link->conf.IntType = INT_MEMORY_AND_IO;
+       p_dev->conf.Attributes = 0;
+       p_dev->conf.IntType = INT_MEMORY_AND_IO;
        
        /* Allocate space for private device-specific data */
        local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
        if (!local) {
                printk(KERN_ERR "airo_cs: no memory for new device\n");
-               kfree (link);
                return -ENOMEM;
        }
-       link->priv = local;
+       p_dev->priv = local;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       airo_config(link);
-
-       return 0;
+       return airo_config(p_dev);
 } /* airo_attach */
 
 /*======================================================================
@@ -198,14 +182,11 @@ static int airo_attach(struct pcmcia_device *p_dev)
   
   ======================================================================*/
 
-static void airo_detach(struct pcmcia_device *p_dev)
+static void airo_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
-
        DEBUG(0, "airo_detach(0x%p)\n", link);
 
-       if (link->state & DEV_CONFIG)
-               airo_release(link);
+       airo_release(link);
 
        if ( ((local_info_t*)link->priv)->eth_dev ) {
                stop_airo_card( ((local_info_t*)link->priv)->eth_dev, 0 );
@@ -213,7 +194,6 @@ static void airo_detach(struct pcmcia_device *p_dev)
        ((local_info_t*)link->priv)->eth_dev = NULL;
 
        kfree(link->priv);
-       kfree(link);
 } /* airo_detach */
 
 /*======================================================================
@@ -227,9 +207,8 @@ static void airo_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void airo_config(dev_link_t *link)
+static int airo_config(struct pcmcia_device *link)
 {
-       client_handle_t handle;
        tuple_t tuple;
        cisparse_t parse;
        local_info_t *dev;
@@ -237,8 +216,7 @@ static void airo_config(dev_link_t *link)
        u_char buf[64];
        win_req_t req;
        memreq_t map;
-       
-       handle = link->handle;
+
        dev = link->priv;
 
        DEBUG(0, "airo_config(0x%p)\n", link);
@@ -252,15 +230,12 @@ static void airo_config(dev_link_t *link)
        tuple.TupleData = buf;
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
-       
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-       
+
        /*
          In this loop, we scan the CIS for configuration table entries,
          each of which describes a valid card configuration, including
@@ -274,12 +249,12 @@ static void airo_config(dev_link_t *link)
          will only use the CIS to fill in implementation-defined details.
        */
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        while (1) {
                cistpl_cftable_entry_t dflt = { 0 };
                cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
-               if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-                               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+               if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+                               pcmcia_parse_tuple(link, &tuple, &parse) != 0)
                        goto next_entry;
                
                if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
@@ -294,16 +269,11 @@ static void airo_config(dev_link_t *link)
                
                /* Use power settings for Vcc and Vpp if present */
                /*  Note that the CIS values need to be rescaled */
-               if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vcc = cfg->vcc.param[CISTPL_POWER_VNOM]/10000;
-               else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vcc = dflt.vcc.param[CISTPL_POWER_VNOM]/10000;
-               
                if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
                else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
                
                /* Do we need to allocate an interrupt? */
@@ -329,12 +299,12 @@ static void airo_config(dev_link_t *link)
                }
                
                /* This reserves IO space but doesn't actually enable it */
-               if (pcmcia_request_io(link->handle, &link->io) != 0)
+               if (pcmcia_request_io(link, &link->io) != 0)
                        goto next_entry;
                
                /*
                  Now set up a common memory window, if needed.  There is room
-                 in the dev_link_t structure for one memory window handle,
+                 in the struct pcmcia_device structure for one memory window handle,
                  but if the base addresses need to be saved, or if multiple
                  windows are needed, the info should go in the private data
                  structure for this device.
@@ -350,7 +320,7 @@ static void airo_config(dev_link_t *link)
                        req.Base = mem->win[0].host_addr;
                        req.Size = mem->win[0].len;
                        req.AccessSpeed = 0;
-                       if (pcmcia_request_window(&link->handle, &req, &link->win) != 0)
+                       if (pcmcia_request_window(&link, &req, &link->win) != 0)
                                goto next_entry;
                        map.Page = 0; map.CardOffset = mem->win[0].card_addr;
                        if (pcmcia_map_mem_page(link->win, &map) != 0)
@@ -360,7 +330,7 @@ static void airo_config(dev_link_t *link)
                break;
                
        next_entry:
-               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
        }
        
     /*
@@ -369,33 +339,32 @@ static void airo_config(dev_link_t *link)
       irq structure is initialized.
     */
        if (link->conf.Attributes & CONF_ENABLE_IRQ)
-               CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+               CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
        
        /*
          This actually configures the PCMCIA socket -- setting up
          the I/O windows and the interrupt mapping, and putting the
          card and host interface into "Memory and IO" mode.
        */
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
        ((local_info_t*)link->priv)->eth_dev = 
                init_airo_card( link->irq.AssignedIRQ,
-                               link->io.BasePort1, 1, &handle_to_dev(handle) );
+                               link->io.BasePort1, 1, &handle_to_dev(link) );
        if (!((local_info_t*)link->priv)->eth_dev) goto cs_failed;
        
        /*
          At this point, the dev_node_t structure(s) need to be
-         initialized and arranged in a linked list at link->dev.
+         initialized and arranged in a linked list at link->dev_node.
        */
        strcpy(dev->node.dev_name, ((local_info_t*)link->priv)->eth_dev->name );
        dev->node.major = dev->node.minor = 0;
-       link->dev = &dev->node;
+       link->dev_node = &dev->node;
        
        /* Finally, report what we've done */
-       printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
-              dev->node.dev_name, link->conf.ConfigIndex,
-              link->conf.Vcc/10, link->conf.Vcc%10);
-       if (link->conf.Vpp1)
-               printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+       printk(KERN_INFO "%s: index 0x%02x: ",
+              dev->node.dev_name, link->conf.ConfigIndex);
+       if (link->conf.Vpp)
+               printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10);
        if (link->conf.Attributes & CONF_ENABLE_IRQ)
                printk(", irq %d", link->irq.AssignedIRQ);
        if (link->io.NumPorts1)
@@ -408,14 +377,12 @@ static void airo_config(dev_link_t *link)
                printk(", mem 0x%06lx-0x%06lx", req.Base,
                       req.Base+req.Size-1);
        printk("\n");
-       
-       link->state &= ~DEV_CONFIG_PENDING;
-       return;
-       
+       return 0;
+
  cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
        airo_release(link);
-       
+       return -ENODEV;
 } /* airo_config */
 
 /*======================================================================
@@ -426,51 +393,26 @@ static void airo_config(dev_link_t *link)
   
   ======================================================================*/
 
-static void airo_release(dev_link_t *link)
+static void airo_release(struct pcmcia_device *link)
 {
        DEBUG(0, "airo_release(0x%p)\n", link);
-       
-       /* Unlink the device chain */
-       link->dev = NULL;
-       
-       /*
-         In a normal driver, additional code may be needed to release
-         other kernel data structures associated with this device. 
-       */
-       
-       /* Don't bother checking to see if these succeed or not */
-       if (link->win)
-               pcmcia_release_window(link->win);
-       pcmcia_release_configuration(link->handle);
-       if (link->io.NumPorts1)
-               pcmcia_release_io(link->handle, &link->io);
-       if (link->irq.AssignedIRQ)
-               pcmcia_release_irq(link->handle, &link->irq);
-       link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
 }
 
-static int airo_suspend(struct pcmcia_device *p_dev)
+static int airo_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        local_info_t *local = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               netif_device_detach(local->eth_dev);
-               pcmcia_release_configuration(link->handle);
-       }
+       netif_device_detach(local->eth_dev);
 
        return 0;
 }
 
-static int airo_resume(struct pcmcia_device *p_dev)
+static int airo_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        local_info_t *local = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
+       if (link->open) {
                reset_airo_card(local->eth_dev);
                netif_device_attach(local->eth_dev);
        }
@@ -492,7 +434,7 @@ static struct pcmcia_driver airo_driver = {
        .drv            = {
                .name   = "airo_cs",
        },
-       .probe          = airo_attach,
+       .probe          = airo_probe,
        .remove         = airo_detach,
        .id_table       = airo_ids,
        .suspend        = airo_suspend,
index d6f4a5a..26bf112 100644 (file)
@@ -91,8 +91,8 @@ MODULE_SUPPORTED_DEVICE("Atmel at76c50x PCMCIA cards");
    event handler. 
 */
 
-static void atmel_config(dev_link_t *link);
-static void atmel_release(dev_link_t *link);
+static int atmel_config(struct pcmcia_device *link);
+static void atmel_release(struct pcmcia_device *link);
 
 /*
    The attach() and detach() entry points are used to create and destroy
@@ -112,10 +112,10 @@ static void atmel_detach(struct pcmcia_device *p_dev);
 /*
    A linked list of "instances" of the  atmelnet device.  Each actual
    PCMCIA card corresponds to one device instance, and is described
-   by one dev_link_t structure (defined in ds.h).
+   by one struct pcmcia_device structure (defined in ds.h).
 
    You may not want to use a linked list for this -- for example, the
-   memory card driver uses an array of dev_link_t pointers, where minor
+   memory card driver uses an array of struct pcmcia_device pointers, where minor
    device numbers are used to derive the corresponding array index.
 */
 
@@ -125,7 +125,7 @@ static void atmel_detach(struct pcmcia_device *p_dev);
    example, ethernet cards, modems).  In other cases, there may be
    many actual or logical devices (SCSI adapters, memory cards with
    multiple partitions).  The dev_node_t structures need to be kept
-   in a linked list starting at the 'dev' field of a dev_link_t
+   in a linked list starting at the 'dev' field of a struct pcmcia_device
    structure.  We allocate them in the card's private data structure,
    because they generally shouldn't be allocated dynamically.
 
@@ -152,24 +152,16 @@ typedef struct local_info_t {
   
   ======================================================================*/
 
-static int atmel_attach(struct pcmcia_device *p_dev)
+static int atmel_probe(struct pcmcia_device *p_dev)
 {
-       dev_link_t *link;
        local_info_t *local;
 
        DEBUG(0, "atmel_attach()\n");
 
-       /* Initialize the dev_link_t structure */
-       link = kzalloc(sizeof(struct dev_link_t), GFP_KERNEL);
-       if (!link) {
-               printk(KERN_ERR "atmel_cs: no memory for new device\n");
-               return -ENOMEM;
-       }
-
        /* Interrupt setup */
-       link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
-       link->irq.IRQInfo1 = IRQ_LEVEL_ID;
-       link->irq.Handler = NULL;
+       p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+       p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+       p_dev->irq.Handler = NULL;
 
        /*
          General socket configuration defaults can go here.  In this
@@ -178,26 +170,18 @@ static int atmel_attach(struct pcmcia_device *p_dev)
          and attributes of IO windows) are fixed by the nature of the
          device, and can be hard-wired here.
        */
-       link->conf.Attributes = 0;
-       link->conf.Vcc = 50;
-       link->conf.IntType = INT_MEMORY_AND_IO;
+       p_dev->conf.Attributes = 0;
+       p_dev->conf.IntType = INT_MEMORY_AND_IO;
 
        /* Allocate space for private device-specific data */
        local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
        if (!local) {
                printk(KERN_ERR "atmel_cs: no memory for new device\n");
-               kfree (link);
                return -ENOMEM;
        }
-       link->priv = local;
+       p_dev->priv = local;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       atmel_config(link);
-
-       return 0;
+       return atmel_config(p_dev);
 } /* atmel_attach */
 
 /*======================================================================
@@ -209,17 +193,13 @@ static int atmel_attach(struct pcmcia_device *p_dev)
   
   ======================================================================*/
 
-static void atmel_detach(struct pcmcia_device *p_dev)
+static void atmel_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
-
        DEBUG(0, "atmel_detach(0x%p)\n", link);
 
-       if (link->state & DEV_CONFIG)
-               atmel_release(link);
+       atmel_release(link);
 
        kfree(link->priv);
-       kfree(link);
 }
 
 /*======================================================================
@@ -236,19 +216,17 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 /* Call-back function to interrogate PCMCIA-specific information
    about the current existance of the card */
 static int card_present(void *arg)
-{ 
-       dev_link_t *link = (dev_link_t *)arg;
-       if (link->state & DEV_SUSPEND)
-               return 0;
-       else if (link->state & DEV_PRESENT)
+{
+       struct pcmcia_device *link = (struct pcmcia_device *)arg;
+
+       if (pcmcia_dev_present(link))
                return 1;
-       
+
        return 0;
 }
 
-static void atmel_config(dev_link_t *link)
+static int atmel_config(struct pcmcia_device *link)
 {
-       client_handle_t handle;
        tuple_t tuple;
        cisparse_t parse;
        local_info_t *dev;
@@ -256,9 +234,8 @@ static void atmel_config(dev_link_t *link)
        u_char buf[64];
        struct pcmcia_device_id *did;
 
-       handle = link->handle;
        dev = link->priv;
-       did = handle_to_dev(handle).driver_data;
+       did = handle_to_dev(link).driver_data;
 
        DEBUG(0, "atmel_config(0x%p)\n", link);
        
@@ -272,15 +249,12 @@ static void atmel_config(dev_link_t *link)
          registers.
        */
        tuple.DesiredTuple = CISTPL_CONFIG;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
-       
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-       
+
        /*
          In this loop, we scan the CIS for configuration table entries,
          each of which describes a valid card configuration, including
@@ -294,12 +268,12 @@ static void atmel_config(dev_link_t *link)
          will only use the CIS to fill in implementation-defined details.
        */
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        while (1) {
                cistpl_cftable_entry_t dflt = { 0 };
                cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
-               if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-                               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+               if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+                               pcmcia_parse_tuple(link, &tuple, &parse) != 0)
                        goto next_entry;
                
                if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
@@ -314,16 +288,11 @@ static void atmel_config(dev_link_t *link)
                
                /* Use power settings for Vcc and Vpp if present */
                /*  Note that the CIS values need to be rescaled */
-               if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vcc = cfg->vcc.param[CISTPL_POWER_VNOM]/10000;
-               else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vcc = dflt.vcc.param[CISTPL_POWER_VNOM]/10000;
-               
                if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
                else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
                
                /* Do we need to allocate an interrupt? */
@@ -349,14 +318,14 @@ static void atmel_config(dev_link_t *link)
                }
                
                /* This reserves IO space but doesn't actually enable it */
-               if (pcmcia_request_io(link->handle, &link->io) != 0)
+               if (pcmcia_request_io(link, &link->io) != 0)
                        goto next_entry;
 
                /* If we got this far, we're cool! */
                break;
                
        next_entry:
-               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
        }
        
        /*
@@ -365,14 +334,14 @@ static void atmel_config(dev_link_t *link)
          irq structure is initialized.
        */
        if (link->conf.Attributes & CONF_ENABLE_IRQ)
-               CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+               CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
        
        /*
          This actually configures the PCMCIA socket -- setting up
          the I/O windows and the interrupt mapping, and putting the
          card and host interface into "Memory and IO" mode.
        */
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
        
        if (link->irq.AssignedIRQ == 0) {
                printk(KERN_ALERT 
@@ -384,7 +353,7 @@ static void atmel_config(dev_link_t *link)
                init_atmel_card(link->irq.AssignedIRQ,
                                link->io.BasePort1,
                                did ? did->driver_info : ATMEL_FW_TYPE_NONE,
-                               &handle_to_dev(handle),
+                               &handle_to_dev(link),
                                card_present, 
                                link);
        if (!((local_info_t*)link->priv)->eth_dev) 
@@ -393,18 +362,18 @@ static void atmel_config(dev_link_t *link)
        
        /*
          At this point, the dev_node_t structure(s) need to be
-         initialized and arranged in a linked list at link->dev.
+         initialized and arranged in a linked list at link->dev_node.
        */
        strcpy(dev->node.dev_name, ((local_info_t*)link->priv)->eth_dev->name );
        dev->node.major = dev->node.minor = 0;
-       link->dev = &dev->node;
-                       
-       link->state &= ~DEV_CONFIG_PENDING;
-       return;
-       
+       link->dev_node = &dev->node;
+
+       return 0;
+
  cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
        atmel_release(link);
+       return -ENODEV;
 }
 
 /*======================================================================
@@ -415,53 +384,34 @@ static void atmel_config(dev_link_t *link)
   
   ======================================================================*/
 
-static void atmel_release(dev_link_t *link)
+static void atmel_release(struct pcmcia_device *link)
 {
        struct net_device *dev = ((local_info_t*)link->priv)->eth_dev;
-               
+
        DEBUG(0, "atmel_release(0x%p)\n", link);
-       
-       /* Unlink the device chain */
-       link->dev = NULL;
-       
-       if (dev) 
+
+       if (dev)
                stop_atmel_card(dev);
-       ((local_info_t*)link->priv)->eth_dev = NULL; 
-       
-       /* Don't bother checking to see if these succeed or not */
-       pcmcia_release_configuration(link->handle);
-       if (link->io.NumPorts1)
-               pcmcia_release_io(link->handle, &link->io);
-       if (link->irq.AssignedIRQ)
-               pcmcia_release_irq(link->handle, &link->irq);
-       link->state &= ~DEV_CONFIG;
+       ((local_info_t*)link->priv)->eth_dev = NULL;
+
+       pcmcia_disable_device(link);
 }
 
-static int atmel_suspend(struct pcmcia_device *dev)
+static int atmel_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        local_info_t *local = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               netif_device_detach(local->eth_dev);
-               pcmcia_release_configuration(link->handle);
-       }
+       netif_device_detach(local->eth_dev);
 
        return 0;
 }
 
-static int atmel_resume(struct pcmcia_device *dev)
+static int atmel_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        local_info_t *local = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               atmel_open(local->eth_dev);
-               netif_device_attach(local->eth_dev);
-       }
+       atmel_open(local->eth_dev);
+       netif_device_attach(local->eth_dev);
 
        return 0;
 }
@@ -515,7 +465,7 @@ static struct pcmcia_driver atmel_driver = {
        .drv            = {
                .name   = "atmel_cs",
         },
-       .probe          = atmel_attach,
+       .probe          = atmel_probe,
        .remove         = atmel_detach,
        .id_table       = atmel_ids,
        .suspend        = atmel_suspend,
index d335b25..55bed92 100644 (file)
@@ -42,7 +42,7 @@ MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry");
 /* struct local_info::hw_priv */
 struct hostap_cs_priv {
        dev_node_t node;
-       dev_link_t *link;
+       struct pcmcia_device *link;
        int sandisk_connectplus;
 };
 
@@ -204,15 +204,13 @@ static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
 
 static void prism2_detach(struct pcmcia_device *p_dev);
 static void prism2_release(u_long arg);
-static int prism2_config(dev_link_t *link);
+static int prism2_config(struct pcmcia_device *link);
 
 
 static int prism2_pccard_card_present(local_info_t *local)
 {
        struct hostap_cs_priv *hw_priv = local->hw_priv;
-       if (hw_priv != NULL && hw_priv->link != NULL &&
-           ((hw_priv->link->state & (DEV_PRESENT | DEV_CONFIG)) ==
-            (DEV_PRESENT | DEV_CONFIG)))
+       if (hw_priv != NULL && hw_priv->link != NULL && pcmcia_dev_present(hw_priv->link))
                return 1;
        return 0;
 }
@@ -237,7 +235,7 @@ static void sandisk_set_iobase(local_info_t *local)
        reg.Action = CS_WRITE;
        reg.Offset = 0x10; /* 0x3f0 IO base 1 */
        reg.Value = hw_priv->link->io.BasePort1 & 0x00ff;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -"
@@ -249,7 +247,7 @@ static void sandisk_set_iobase(local_info_t *local)
        reg.Action = CS_WRITE;
        reg.Offset = 0x12; /* 0x3f2 IO base 2 */
        reg.Value = (hw_priv->link->io.BasePort1 & 0xff00) >> 8;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -"
@@ -301,9 +299,9 @@ static int sandisk_enable_wireless(struct net_device *dev)
        tuple.TupleData = buf;
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
-       if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) ||
-           pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) ||
-           pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) ||
+       if (pcmcia_get_first_tuple(hw_priv->link, &tuple) ||
+           pcmcia_get_tuple_data(hw_priv->link, &tuple) ||
+           pcmcia_parse_tuple(hw_priv->link, &tuple, parse) ||
            parse->manfid.manf != 0xd601 || parse->manfid.card != 0x0101) {
                /* No SanDisk manfid found */
                ret = -ENODEV;
@@ -311,9 +309,9 @@ static int sandisk_enable_wireless(struct net_device *dev)
        }
 
        tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
-       if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) ||
-           pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) ||
-           pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) ||
+       if (pcmcia_get_first_tuple(hw_priv->link, &tuple) ||
+           pcmcia_get_tuple_data(hw_priv->link, &tuple) ||
+           pcmcia_parse_tuple(hw_priv->link, &tuple, parse) ||
                parse->longlink_mfc.nfn < 2) {
                /* No multi-function links found */
                ret = -ENODEV;
@@ -328,7 +326,7 @@ static int sandisk_enable_wireless(struct net_device *dev)
        reg.Action = CS_WRITE;
        reg.Offset = CISREG_COR;
        reg.Value = COR_SOFT_RESET;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
@@ -345,7 +343,7 @@ static int sandisk_enable_wireless(struct net_device *dev)
         * will be enabled during the first cor_sreset call.
         */
        reg.Value = COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | COR_FUNC_ENA;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
@@ -380,7 +378,7 @@ static void prism2_pccard_cor_sreset(local_info_t *local)
        reg.Action = CS_READ;
        reg.Offset = CISREG_COR;
        reg.Value = 0;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n",
@@ -392,7 +390,7 @@ static void prism2_pccard_cor_sreset(local_info_t *local)
 
        reg.Action = CS_WRITE;
        reg.Value |= COR_SOFT_RESET;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n",
@@ -405,7 +403,7 @@ static void prism2_pccard_cor_sreset(local_info_t *local)
        reg.Value &= ~COR_SOFT_RESET;
        if (hw_priv->sandisk_connectplus)
                reg.Value |= COR_IREQ_ENA;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n",
@@ -439,7 +437,7 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
        reg.Action = CS_READ;
        reg.Offset = CISREG_COR;
        reg.Value = 0;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 "
@@ -452,7 +450,7 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
 
        reg.Action = CS_WRITE;
        reg.Value |= COR_SOFT_RESET;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 "
@@ -466,7 +464,7 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
        reg.Action = CS_WRITE;
        reg.Value = hcr;
        reg.Offset = CISREG_CCSR;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 "
@@ -478,7 +476,7 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
        reg.Action = CS_WRITE;
        reg.Offset = CISREG_COR;
        reg.Value = old_cor & ~COR_SOFT_RESET;
-       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+       res = pcmcia_access_configuration_register(hw_priv->link,
                                                   &reg);
        if (res != CS_SUCCESS) {
                printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 "
@@ -501,40 +499,27 @@ static struct prism2_helper_functions prism2_pccard_funcs =
 
 /* allocate local data and register with CardServices
  * initialize dev_link structure, but do not configure the card yet */
-static int prism2_attach(struct pcmcia_device *p_dev)
+static int hostap_cs_probe(struct pcmcia_device *p_dev)
 {
-       dev_link_t *link;
-
-       link = kmalloc(sizeof(dev_link_t), GFP_KERNEL);
-       if (link == NULL)
-               return -ENOMEM;
-
-       memset(link, 0, sizeof(dev_link_t));
+       int ret;
 
        PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info);
-       link->conf.Vcc = 33;
-       link->conf.IntType = INT_MEMORY_AND_IO;
-
-       link->handle = p_dev;
-       p_dev->instance = link;
+       p_dev->conf.IntType = INT_MEMORY_AND_IO;
 
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       if (prism2_config(link))
+       ret = prism2_config(p_dev);
+       if (ret) {
                PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n");
+       }
 
-       return 0;
+       return ret;
 }
 
 
-static void prism2_detach(struct pcmcia_device *p_dev)
+static void prism2_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
-
        PDEBUG(DEBUG_FLOW, "prism2_detach\n");
 
-       if (link->state & DEV_CONFIG) {
-               prism2_release((u_long)link);
-       }
+       prism2_release((u_long)link);
 
        /* release net devices */
        if (link->priv) {
@@ -547,7 +532,6 @@ static void prism2_detach(struct pcmcia_device *p_dev)
                prism2_free_local_data(dev);
                kfree(hw_priv);
        }
-       kfree(link);
 }
 
 
@@ -558,7 +542,7 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 do { int ret = (retf); \
 if (ret != 0) { \
        PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \
-       cs_error(link->handle, fn, ret); \
+       cs_error(link, fn, ret); \
        goto next_entry; \
 } \
 } while (0)
@@ -566,7 +550,7 @@ if (ret != 0) { \
 
 /* run after a CARD_INSERTION event is received to configure the PCMCIA
  * socket and make the device available to the system */
-static int prism2_config(dev_link_t *link)
+static int prism2_config(struct pcmcia_device *link)
 {
        struct net_device *dev;
        struct hostap_interface *iface;
@@ -595,27 +579,24 @@ static int prism2_config(dev_link_t *link)
        tuple.TupleData = buf;
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link->handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link->handle, &tuple, parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, parse));
        link->conf.ConfigBase = parse->config.base;
        link->conf.Present = parse->config.rmask[0];
 
        CS_CHECK(GetConfigurationInfo,
-                pcmcia_get_configuration_info(link->handle, &conf));
-       PDEBUG(DEBUG_HW, "%s: %s Vcc=%d (from config)\n", dev_info,
-              ignore_cis_vcc ? "ignoring" : "setting", conf.Vcc);
-       link->conf.Vcc = conf.Vcc;
+                pcmcia_get_configuration_info(link, &conf));
 
        /* Look for an appropriate configuration table entry in the CIS */
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        for (;;) {
                cistpl_cftable_entry_t *cfg = &(parse->cftable_entry);
                CFG_CHECK2(GetTupleData,
-                          pcmcia_get_tuple_data(link->handle, &tuple));
+                          pcmcia_get_tuple_data(link, &tuple));
                CFG_CHECK2(ParseTuple,
-                          pcmcia_parse_tuple(link->handle, &tuple, parse));
+                          pcmcia_parse_tuple(link, &tuple, parse));
 
                if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
                        dflt = *cfg;
@@ -650,10 +631,10 @@ static int prism2_config(dev_link_t *link)
                }
 
                if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
                else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
 
                /* Do we need to allocate an interrupt? */
@@ -695,19 +676,19 @@ static int prism2_config(dev_link_t *link)
 
                /* This reserves IO space but doesn't actually enable it */
                CFG_CHECK2(RequestIO,
-                          pcmcia_request_io(link->handle, &link->io));
+                          pcmcia_request_io(link, &link->io));
 
                /* This configuration table entry is OK */
                break;
 
        next_entry:
                CS_CHECK(GetNextTuple,
-                        pcmcia_get_next_tuple(link->handle, &tuple));
+                        pcmcia_get_next_tuple(link, &tuple));
        }
 
        /* Need to allocate net_device before requesting IRQ handler */
        dev = prism2_init_local_data(&prism2_pccard_funcs, 0,
-                                    &handle_to_dev(link->handle));
+                                    &handle_to_dev(link));
        if (dev == NULL)
                goto failed;
        link->priv = dev;
@@ -717,7 +698,7 @@ static int prism2_config(dev_link_t *link)
        local->hw_priv = hw_priv;
        hw_priv->link = link;
        strcpy(hw_priv->node.dev_name, dev->name);
-       link->dev = &hw_priv->node;
+       link->dev_node = &hw_priv->node;
 
        /*
         * Allocate an interrupt line.  Note that this does not assign a
@@ -730,7 +711,7 @@ static int prism2_config(dev_link_t *link)
                link->irq.Handler = prism2_interrupt;
                link->irq.Instance = dev;
                CS_CHECK(RequestIRQ,
-                        pcmcia_request_irq(link->handle, &link->irq));
+                        pcmcia_request_irq(link, &link->irq));
        }
 
        /*
@@ -739,18 +720,17 @@ static int prism2_config(dev_link_t *link)
         * card and host interface into "Memory and IO" mode.
         */
        CS_CHECK(RequestConfiguration,
-                pcmcia_request_configuration(link->handle, &link->conf));
+                pcmcia_request_configuration(link, &link->conf));
 
        dev->irq = link->irq.AssignedIRQ;
        dev->base_addr = link->io.BasePort1;
 
        /* Finally, report what we've done */
-       printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
-              dev_info, link->conf.ConfigIndex,
-              link->conf.Vcc / 10, link->conf.Vcc % 10);
-       if (link->conf.Vpp1)
-               printk(", Vpp %d.%d", link->conf.Vpp1 / 10,
-                      link->conf.Vpp1 % 10);
+       printk(KERN_INFO "%s: index 0x%02x: ",
+              dev_info, link->conf.ConfigIndex);
+       if (link->conf.Vpp)
+               printk(", Vpp %d.%d", link->conf.Vpp / 10,
+                      link->conf.Vpp % 10);
        if (link->conf.Attributes & CONF_ENABLE_IRQ)
                printk(", irq %d", link->irq.AssignedIRQ);
        if (link->io.NumPorts1)
@@ -761,9 +741,6 @@ static int prism2_config(dev_link_t *link)
                       link->io.BasePort2+link->io.NumPorts2-1);
        printk("\n");
 
-       link->state |= DEV_CONFIG;
-       link->state &= ~DEV_CONFIG_PENDING;
-
        local->shutdown = 0;
 
        sandisk_enable_wireless(dev);
@@ -778,7 +755,7 @@ static int prism2_config(dev_link_t *link)
        return ret;
 
  cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 
  failed:
        kfree(parse);
@@ -790,7 +767,7 @@ static int prism2_config(dev_link_t *link)
 
 static void prism2_release(u_long arg)
 {
-       dev_link_t *link = (dev_link_t *)arg;
+       struct pcmcia_device *link = (struct pcmcia_device *)arg;
 
        PDEBUG(DEBUG_FLOW, "prism2_release\n");
 
@@ -799,71 +776,54 @@ static void prism2_release(u_long arg)
                struct hostap_interface *iface;
 
                iface = netdev_priv(dev);
-               if (link->state & DEV_CONFIG)
-                       prism2_hw_shutdown(dev, 0);
+               prism2_hw_shutdown(dev, 0);
                iface->local->shutdown = 1;
        }
 
-       if (link->win)
-               pcmcia_release_window(link->win);
-       pcmcia_release_configuration(link->handle);
-       if (link->io.NumPorts1)
-               pcmcia_release_io(link->handle, &link->io);
-       if (link->irq.AssignedIRQ)
-               pcmcia_release_irq(link->handle, &link->irq);
-
-       link->state &= ~DEV_CONFIG;
-
+       pcmcia_disable_device(link);
        PDEBUG(DEBUG_FLOW, "release - done\n");
 }
 
-static int hostap_cs_suspend(struct pcmcia_device *p_dev)
+static int hostap_cs_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = (struct net_device *) link->priv;
        int dev_open = 0;
+       struct hostap_interface *iface = NULL;
 
-       PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info);
-
-       link->state |= DEV_SUSPEND;
+       if (dev)
+               iface = netdev_priv(dev);
 
-       if (link->state & DEV_CONFIG) {
-               struct hostap_interface *iface = netdev_priv(dev);
-               if (iface && iface->local)
-                       dev_open = iface->local->num_dev_open > 0;
-               if (dev_open) {
-                       netif_stop_queue(dev);
-                       netif_device_detach(dev);
-               }
-               prism2_suspend(dev);
-               pcmcia_release_configuration(link->handle);
+       PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info);
+       if (iface && iface->local)
+               dev_open = iface->local->num_dev_open > 0;
+       if (dev_open) {
+               netif_stop_queue(dev);
+               netif_device_detach(dev);
        }
+       prism2_suspend(dev);
 
        return 0;
 }
 
-static int hostap_cs_resume(struct pcmcia_device *p_dev)
+static int hostap_cs_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = (struct net_device *) link->priv;
        int dev_open = 0;
+       struct hostap_interface *iface = NULL;
 
-       PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info);
+       if (dev)
+               iface = netdev_priv(dev);
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               struct hostap_interface *iface = netdev_priv(dev);
-               if (iface && iface->local)
-                       dev_open = iface->local->num_dev_open > 0;
+       PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info);
 
-               pcmcia_request_configuration(link->handle, &link->conf);
+       if (iface && iface->local)
+               dev_open = iface->local->num_dev_open > 0;
 
-               prism2_hw_shutdown(dev, 1);
-               prism2_hw_config(dev, dev_open ? 0 : 1);
-               if (dev_open) {
-                       netif_device_attach(dev);
-                       netif_start_queue(dev);
-               }
+       prism2_hw_shutdown(dev, 1);
+       prism2_hw_config(dev, dev_open ? 0 : 1);
+       if (dev_open) {
+               netif_device_attach(dev);
+               netif_start_queue(dev);
        }
 
        return 0;
@@ -930,7 +890,7 @@ static struct pcmcia_driver hostap_driver = {
        .drv            = {
                .name   = "hostap_cs",
        },
-       .probe          = prism2_attach,
+       .probe          = hostap_cs_probe,
        .remove         = prism2_detach,
        .owner          = THIS_MODULE,
        .id_table       = hostap_cs_ids,
index 9dce522..bca89cf 100644 (file)
@@ -5573,8 +5573,7 @@ static void ipw_adhoc_create(struct ipw_priv *priv,
        case IEEE80211_52GHZ_BAND:
                network->mode = IEEE_A;
                i = ieee80211_channel_to_index(priv->ieee, priv->channel);
-               if (i == -1)
-                       BUG();
+               BUG_ON(i == -1);
                if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
                        IPW_WARNING("Overriding invalid channel\n");
                        priv->channel = geo->a[0].channel;
@@ -5587,8 +5586,7 @@ static void ipw_adhoc_create(struct ipw_priv *priv,
                else
                        network->mode = IEEE_B;
                i = ieee80211_channel_to_index(priv->ieee, priv->channel);
-               if (i == -1)
-                       BUG();
+               BUG_ON(i == -1);
                if (geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
                        IPW_WARNING("Overriding invalid channel\n");
                        priv->channel = geo->bg[0].channel;
@@ -6715,8 +6713,7 @@ static int ipw_qos_association(struct ipw_priv *priv,
 
        switch (priv->ieee->iw_mode) {
        case IW_MODE_ADHOC:
-               if (!(network->capability & WLAN_CAPABILITY_IBSS))
-                       BUG();
+               BUG_ON(!(network->capability & WLAN_CAPABILITY_IBSS));
 
                qos_data = &ibss_data;
                break;
index 75ce6dd..9343d97 100644 (file)
@@ -190,8 +190,8 @@ module_param(mem_speed, int, 0);
 /*====================================================================*/
 
 /* PCMCIA (Card Services) related functions */
-static void netwave_release(dev_link_t *link);     /* Card removal */
-static void netwave_pcmcia_config(dev_link_t *arg); /* Runs after card 
+static void netwave_release(struct pcmcia_device *link);     /* Card removal */
+static int netwave_pcmcia_config(struct pcmcia_device *arg); /* Runs after card
                                                                                                           insertion */
 static void netwave_detach(struct pcmcia_device *p_dev);    /* Destroy instance */
 
@@ -221,10 +221,10 @@ static struct iw_statistics* netwave_get_wireless_stats(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
 
 /*
-   A dev_link_t structure has fields for most things that are needed
+   A struct pcmcia_device structure has fields for most things that are needed
    to keep track of a socket, but there will usually be some device
    specific information that also needs to be kept track of.  The
-   'priv' pointer in a dev_link_t structure can be used to point to
+   'priv' pointer in a struct pcmcia_device structure can be used to point to
    a device-specific private data structure, like this.
 
    A driver needs to provide a dev_node_t structure for each device
@@ -232,7 +232,7 @@ static void set_multicast_list(struct net_device *dev);
    example, ethernet cards, modems).  In other cases, there may be
    many actual or logical devices (SCSI adapters, memory cards with
    multiple partitions).  The dev_node_t structures need to be kept
-   in a linked list starting at the 'dev' field of a dev_link_t
+   in a linked list starting at the 'dev' field of a struct pcmcia_device
    structure.  We allocate them in the card's private data structure,
    because they generally can't be allocated dynamically.
 */
@@ -268,7 +268,7 @@ struct site_survey {
 };     
    
 typedef struct netwave_private {
-    dev_link_t link;
+       struct pcmcia_device    *p_dev;
     spinlock_t spinlock;       /* Serialize access to the hardware (SMP) */
     dev_node_t node;
     u_char     __iomem *ramBase;
@@ -376,20 +376,19 @@ static struct iw_statistics *netwave_get_wireless_stats(struct net_device *dev)
  *     configure the card at this point -- we wait until we receive a
  *     card insertion event.
  */
-static int netwave_attach(struct pcmcia_device *p_dev)
+static int netwave_probe(struct pcmcia_device *link)
 {
-    dev_link_t *link;
     struct net_device *dev;
     netwave_private *priv;
 
     DEBUG(0, "netwave_attach()\n");
 
-    /* Initialize the dev_link_t structure */
+    /* Initialize the struct pcmcia_device structure */
     dev = alloc_etherdev(sizeof(netwave_private));
     if (!dev)
        return -ENOMEM;
     priv = netdev_priv(dev);
-    link = &priv->link;
+    priv->p_dev = link;
     link->priv = dev;
 
     /* The io structure describes IO port mapping */
@@ -406,7 +405,6 @@ static int netwave_attach(struct pcmcia_device *p_dev)
     
     /* General socket configuration */
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
     link->conf.ConfigIndex = 1;
     link->conf.Present = PRESENT_OPTION;
@@ -430,13 +428,7 @@ static int netwave_attach(struct pcmcia_device *p_dev)
     dev->stop = &netwave_close;
     link->irq.Instance = dev;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    netwave_pcmcia_config( link);
-
-    return 0;
+    return netwave_pcmcia_config( link);
 } /* netwave_attach */
 
 /*
@@ -447,17 +439,15 @@ static int netwave_attach(struct pcmcia_device *p_dev)
  *    structures are freed.  Otherwise, the structures will be freed
  *    when the device is released.
  */
-static void netwave_detach(struct pcmcia_device *p_dev)
+static void netwave_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
        DEBUG(0, "netwave_detach(0x%p)\n", link);
 
-       if (link->state & DEV_CONFIG)
-               netwave_release(link);
+       netwave_release(link);
 
-       if (link->dev)
+       if (link->dev_node)
                unregister_netdev(dev);
 
        free_netdev(dev);
@@ -743,8 +733,7 @@ static const struct iw_handler_def  netwave_handler_def =
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void netwave_pcmcia_config(dev_link_t *link) {
-    client_handle_t handle = link->handle;
+static int netwave_pcmcia_config(struct pcmcia_device *link) {
     struct net_device *dev = link->priv;
     netwave_private *priv = netdev_priv(dev);
     tuple_t tuple;
@@ -766,15 +755,12 @@ static void netwave_pcmcia_config(dev_link_t *link) {
     tuple.TupleDataMax = 64;
     tuple.TupleOffset = 0;
     tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present = parse.config.rmask[0];
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
     /*
      *  Try allocating IO ports.  This tries a few fixed addresses.
      *  If you want, you can also read the card's config table to
@@ -782,11 +768,11 @@ static void netwave_pcmcia_config(dev_link_t *link) {
      */
     for (i = j = 0x0; j < 0x400; j += 0x20) {
        link->io.BasePort1 = j ^ 0x300;
-       i = pcmcia_request_io(link->handle, &link->io);
+       i = pcmcia_request_io(link, &link->io);
        if (i == CS_SUCCESS) break;
     }
     if (i != CS_SUCCESS) {
-       cs_error(link->handle, RequestIO, i);
+       cs_error(link, RequestIO, i);
        goto failed;
     }
 
@@ -794,16 +780,16 @@ static void netwave_pcmcia_config(dev_link_t *link) {
      *  Now allocate an interrupt line.  Note that this does not
      *  actually assign a handler to the interrupt.
      */
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
 
     /*
      *  This actually configures the PCMCIA socket -- setting up
      *  the I/O windows and the interrupt mapping.
      */
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
     /*
-     *  Allocate a 32K memory window.  Note that the dev_link_t
+     *  Allocate a 32K memory window.  Note that the struct pcmcia_device
      *  structure provides space for one window handle -- if your
      *  device needs several windows, you'll need to keep track of
      *  the handles in your private data structure, dev->priv.
@@ -813,7 +799,7 @@ static void netwave_pcmcia_config(dev_link_t *link) {
     req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
     req.Base = 0; req.Size = 0x8000;
     req.AccessSpeed = mem_speed;
-    CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &link->win));
+    CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win));
     mem.CardOffset = 0x20000; mem.Page = 0; 
     CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem));
 
@@ -823,7 +809,7 @@ static void netwave_pcmcia_config(dev_link_t *link) {
 
     dev->irq = link->irq.AssignedIRQ;
     dev->base_addr = link->io.BasePort1;
-    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+    SET_NETDEV_DEV(dev, &handle_to_dev(link));
 
     if (register_netdev(dev) != 0) {
        printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n");
@@ -831,8 +817,7 @@ static void netwave_pcmcia_config(dev_link_t *link) {
     }
 
     strcpy(priv->node.dev_name, dev->name);
-    link->dev = &priv->node;
-    link->state &= ~DEV_CONFIG_PENDING;
+    link->dev_node = &priv->node;
 
     /* Reset card before reading physical address */
     netwave_doreset(dev->base_addr, ramBase);
@@ -852,12 +837,13 @@ static void netwave_pcmcia_config(dev_link_t *link) {
     printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n", 
           get_uint16(ramBase + NETWAVE_EREG_ARW),
           get_uint16(ramBase + NETWAVE_EREG_ARW+2));
-    return;
+    return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 failed:
     netwave_release(link);
+    return -ENODEV;
 } /* netwave_pcmcia_config */
 
 /*
@@ -867,52 +853,35 @@ failed:
  *    device, and release the PCMCIA configuration.  If the device is
  *    still open, this will be postponed until it is closed.
  */
-static void netwave_release(dev_link_t *link)
+static void netwave_release(struct pcmcia_device *link)
 {
-    struct net_device *dev = link->priv;
-    netwave_private *priv = netdev_priv(dev);
-
-    DEBUG(0, "netwave_release(0x%p)\n", link);
+       struct net_device *dev = link->priv;
+       netwave_private *priv = netdev_priv(dev);
 
-    /* Don't bother checking to see if these succeed or not */
-    if (link->win) {
-       iounmap(priv->ramBase);
-       pcmcia_release_window(link->win);
-    }
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
+       DEBUG(0, "netwave_release(0x%p)\n", link);
 
-    link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
+       if (link->win)
+               iounmap(priv->ramBase);
 }
 
-static int netwave_suspend(struct pcmcia_device *p_dev)
+static int netwave_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-       }
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int netwave_resume(struct pcmcia_device *p_dev)
+static int netwave_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       netwave_reset(dev);
-                       netif_device_attach(dev);
-               }
+       if (link->open) {
+               netwave_reset(dev);
+               netif_device_attach(dev);
        }
 
        return 0;
@@ -1119,7 +1088,7 @@ static irqreturn_t netwave_interrupt(int irq, void* dev_id, struct pt_regs *regs
     u_char __iomem *ramBase;
     struct net_device *dev = (struct net_device *)dev_id;
     struct netwave_private *priv = netdev_priv(dev);
-    dev_link_t *link = &priv->link;
+    struct pcmcia_device *link = priv->p_dev;
     int i;
     
     if (!netif_device_present(dev))
@@ -1138,7 +1107,7 @@ static irqreturn_t netwave_interrupt(int irq, void* dev_id, struct pt_regs *regs
        
         status = inb(iobase + NETWAVE_REG_ASR);
                
-       if (!DEV_OK(link)) {
+       if (!pcmcia_dev_present(link)) {
            DEBUG(1, "netwave_interrupt: Interrupt with status 0x%x "
                  "from removed or suspended card!\n", status);
            break;
@@ -1373,11 +1342,11 @@ static int netwave_rx(struct net_device *dev)
 
 static int netwave_open(struct net_device *dev) {
     netwave_private *priv = netdev_priv(dev);
-    dev_link_t *link = &priv->link;
+    struct pcmcia_device *link = priv->p_dev;
 
     DEBUG(1, "netwave_open: starting.\n");
     
-    if (!DEV_OK(link))
+    if (!pcmcia_dev_present(link))
        return -ENODEV;
 
     link->open++;
@@ -1390,7 +1359,7 @@ static int netwave_open(struct net_device *dev) {
 
 static int netwave_close(struct net_device *dev) {
     netwave_private *priv = netdev_priv(dev);
-    dev_link_t *link = &priv->link;
+    struct pcmcia_device *link = priv->p_dev;
 
     DEBUG(1, "netwave_close: finishing.\n");
 
@@ -1411,7 +1380,7 @@ static struct pcmcia_driver netwave_driver = {
        .drv            = {
                .name   = "netwave_cs",
        },
-       .probe          = netwave_attach,
+       .probe          = netwave_probe,
        .remove         = netwave_detach,
        .id_table       = netwave_ids,
        .suspend        = netwave_suspend,
index ec6f2a4..434f7d7 100644 (file)
@@ -49,7 +49,7 @@ MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket
 /* PCMCIA specific device information (goes in the card field of
  * struct orinoco_private */
 struct orinoco_pccard {
-       dev_link_t link;
+       struct pcmcia_device    *p_dev;
        dev_node_t node;
 
        /* Used to handle hard reset */
@@ -63,8 +63,8 @@ struct orinoco_pccard {
 /* Function prototypes                                             */
 /********************************************************************/
 
-static void orinoco_cs_config(dev_link_t *link);
-static void orinoco_cs_release(dev_link_t *link);
+static int orinoco_cs_config(struct pcmcia_device *link);
+static void orinoco_cs_release(struct pcmcia_device *link);
 static void orinoco_cs_detach(struct pcmcia_device *p_dev);
 
 /********************************************************************/
@@ -75,13 +75,13 @@ static int
 orinoco_cs_hard_reset(struct orinoco_private *priv)
 {
        struct orinoco_pccard *card = priv->card;
-       dev_link_t *link = &card->link;
+       struct pcmcia_device *link = card->p_dev;
        int err;
 
        /* We need atomic ops here, because we're not holding the lock */
        set_bit(0, &card->hard_reset_in_progress);
 
-       err = pcmcia_reset_card(link->handle, NULL);
+       err = pcmcia_reset_card(link, NULL);
        if (err)
                return err;
 
@@ -104,12 +104,11 @@ orinoco_cs_hard_reset(struct orinoco_private *priv)
  * configure the card at this point -- we wait until we receive a card
  * insertion event.  */
 static int
-orinoco_cs_attach(struct pcmcia_device *p_dev)
+orinoco_cs_probe(struct pcmcia_device *link)
 {
        struct net_device *dev;
        struct orinoco_private *priv;
        struct orinoco_pccard *card;
-       dev_link_t *link;
 
        dev = alloc_orinocodev(sizeof(*card), orinoco_cs_hard_reset);
        if (! dev)
@@ -118,7 +117,7 @@ orinoco_cs_attach(struct pcmcia_device *p_dev)
        card = priv->card;
 
        /* Link both structures together */
-       link = &card->link;
+       card->p_dev = link;
        link->priv = dev;
 
        /* Interrupt setup */
@@ -135,16 +134,7 @@ orinoco_cs_attach(struct pcmcia_device *p_dev)
        link->conf.Attributes = 0;
        link->conf.IntType = INT_MEMORY_AND_IO;
 
-       /* Register with Card Services */
-       link->next = NULL;
-
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       orinoco_cs_config(link);
-
-       return 0;
+       return orinoco_cs_config(link);
 }                              /* orinoco_cs_attach */
 
 /*
@@ -153,16 +143,14 @@ orinoco_cs_attach(struct pcmcia_device *p_dev)
  * are freed.  Otherwise, the structures will be freed when the device
  * is released.
  */
-static void orinoco_cs_detach(struct pcmcia_device *p_dev)
+static void orinoco_cs_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       if (link->state & DEV_CONFIG)
-               orinoco_cs_release(link);
+       orinoco_cs_release(link);
 
-       DEBUG(0, PFX "detach: link=%p link->dev=%p\n", link, link->dev);
-       if (link->dev) {
+       DEBUG(0, PFX "detach: link=%p link->dev_node=%p\n", link, link->dev_node);
+       if (link->dev_node) {
                DEBUG(0, PFX "About to unregister net device %p\n",
                      dev);
                unregister_netdev(dev);
@@ -180,11 +168,10 @@ static void orinoco_cs_detach(struct pcmcia_device *p_dev)
                last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; \
        } while (0)
 
-static void
-orinoco_cs_config(dev_link_t *link)
+static int
+orinoco_cs_config(struct pcmcia_device *link)
 {
        struct net_device *dev = link->priv;
-       client_handle_t handle = link->handle;
        struct orinoco_private *priv = netdev_priv(dev);
        struct orinoco_pccard *card = priv->card;
        hermes_t *hw = &priv->hw;
@@ -196,7 +183,7 @@ orinoco_cs_config(dev_link_t *link)
        cisparse_t parse;
        void __iomem *mem;
 
-       CS_CHECK(ValidateCIS, pcmcia_validate_cis(handle, &info));
+       CS_CHECK(ValidateCIS, pcmcia_validate_cis(link, &info));
 
        /*
         * This reads the card's CONFIG tuple to find its
@@ -207,19 +194,15 @@ orinoco_cs_config(dev_link_t *link)
        tuple.TupleData = buf;
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
        /* Look up the current Vcc */
        CS_CHECK(GetConfigurationInfo,
-                pcmcia_get_configuration_info(handle, &conf));
-       link->conf.Vcc = conf.Vcc;
+                pcmcia_get_configuration_info(link, &conf));
 
        /*
         * In this loop, we scan the CIS for configuration table
@@ -236,13 +219,13 @@ orinoco_cs_config(dev_link_t *link)
         * implementation-defined details.
         */
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        while (1) {
                cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
                cistpl_cftable_entry_t dflt = { .index = 0 };
 
-               if ( (pcmcia_get_tuple_data(handle, &tuple) != 0)
-                   || (pcmcia_parse_tuple(handle, &tuple, &parse) != 0))
+               if ( (pcmcia_get_tuple_data(link, &tuple) != 0)
+                   || (pcmcia_parse_tuple(link, &tuple, &parse) != 0))
                        goto next_entry;
 
                if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
@@ -274,10 +257,10 @@ orinoco_cs_config(dev_link_t *link)
                }
 
                if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                            cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
                else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                            dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
                
                /* Do we need to allocate an interrupt? */
@@ -307,7 +290,7 @@ orinoco_cs_config(dev_link_t *link)
                        }
 
                        /* This reserves IO space but doesn't actually enable it */
-                       if (pcmcia_request_io(link->handle, &link->io) != 0)
+                       if (pcmcia_request_io(link, &link->io) != 0)
                                goto next_entry;
                }
 
@@ -317,9 +300,8 @@ orinoco_cs_config(dev_link_t *link)
                break;
                
        next_entry:
-               if (link->io.NumPorts1)
-                       pcmcia_release_io(link->handle, &link->io);
-               last_ret = pcmcia_get_next_tuple(handle, &tuple);
+               pcmcia_disable_device(link);
+               last_ret = pcmcia_get_next_tuple(link, &tuple);
                if (last_ret  == CS_NO_MORE_ITEMS) {
                        printk(KERN_ERR PFX "GetNextTuple(): No matching "
                               "CIS configuration.  Maybe you need the "
@@ -333,7 +315,7 @@ orinoco_cs_config(dev_link_t *link)
         * a handler to the interrupt, unless the 'Handler' member of
         * the irq structure is initialized.
         */
-       CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+       CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
 
        /* We initialize the hermes structure before completing PCMCIA
         * configuration just in case the interrupt handler gets
@@ -350,7 +332,7 @@ orinoco_cs_config(dev_link_t *link)
         * card and host interface into "Memory and IO" mode.
         */
        CS_CHECK(RequestConfiguration,
-                pcmcia_request_configuration(link->handle, &link->conf));
+                pcmcia_request_configuration(link, &link->conf));
 
        /* Ok, we have the configuration, prepare to register the netdev */
        dev->base_addr = link->io.BasePort1;
@@ -358,7 +340,7 @@ orinoco_cs_config(dev_link_t *link)
        SET_MODULE_OWNER(dev);
        card->node.major = card->node.minor = 0;
 
-       SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+       SET_NETDEV_DEV(dev, &handle_to_dev(link));
        /* Tell the stack we exist */
        if (register_netdev(dev) != 0) {
                printk(KERN_ERR PFX "register_netdev() failed\n");
@@ -366,20 +348,18 @@ orinoco_cs_config(dev_link_t *link)
        }
 
        /* At this point, the dev_node_t structure(s) needs to be
-        * initialized and arranged in a linked list at link->dev. */
+        * initialized and arranged in a linked list at link->dev_node. */
        strcpy(card->node.dev_name, dev->name);
-       link->dev = &card->node; /* link->dev being non-NULL is also
+       link->dev_node = &card->node; /* link->dev_node being non-NULL is also
                                     used to indicate that the
                                     net_device has been registered */
-       link->state &= ~DEV_CONFIG_PENDING;
 
        /* Finally, report what we've done */
-       printk(KERN_DEBUG "%s: index 0x%02x: Vcc %d.%d",
-              dev->name, link->conf.ConfigIndex,
-              link->conf.Vcc / 10, link->conf.Vcc % 10);
-       if (link->conf.Vpp1)
-               printk(", Vpp %d.%d", link->conf.Vpp1 / 10,
-                      link->conf.Vpp1 % 10);
+       printk(KERN_DEBUG "%s: index 0x%02x: ",
+              dev->name, link->conf.ConfigIndex);
+       if (link->conf.Vpp)
+               printk(", Vpp %d.%d", link->conf.Vpp / 10,
+                      link->conf.Vpp % 10);
        printk(", irq %d", link->irq.AssignedIRQ);
        if (link->io.NumPorts1)
                printk(", io 0x%04x-0x%04x", link->io.BasePort1,
@@ -389,13 +369,14 @@ orinoco_cs_config(dev_link_t *link)
                       link->io.BasePort2 + link->io.NumPorts2 - 1);
        printk("\n");
 
-       return;
+       return 0;
 
  cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 
  failed:
        orinoco_cs_release(link);
+       return -ENODEV;
 }                              /* orinoco_cs_config */
 
 /*
@@ -404,7 +385,7 @@ orinoco_cs_config(dev_link_t *link)
  * still open, this will be postponed until it is closed.
  */
 static void
-orinoco_cs_release(dev_link_t *link)
+orinoco_cs_release(struct pcmcia_device *link)
 {
        struct net_device *dev = link->priv;
        struct orinoco_private *priv = netdev_priv(dev);
@@ -416,88 +397,68 @@ orinoco_cs_release(dev_link_t *link)
        priv->hw_unavailable++;
        spin_unlock_irqrestore(&priv->lock, flags);
 
-       /* Don't bother checking to see if these succeed or not */
-       pcmcia_release_configuration(link->handle);
-       if (link->io.NumPorts1)
-               pcmcia_release_io(link->handle, &link->io);
-       if (link->irq.AssignedIRQ)
-               pcmcia_release_irq(link->handle, &link->irq);
-       link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
        if (priv->hw.iobase)
                ioport_unmap(priv->hw.iobase);
 }                              /* orinoco_cs_release */
 
-static int orinoco_cs_suspend(struct pcmcia_device *p_dev)
+static int orinoco_cs_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
        struct orinoco_private *priv = netdev_priv(dev);
        struct orinoco_pccard *card = priv->card;
        int err = 0;
        unsigned long flags;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               /* This is probably racy, but I can't think of
-                  a better way, short of rewriting the PCMCIA
-                  layer to not suck :-( */
-               if (! test_bit(0, &card->hard_reset_in_progress)) {
-                       spin_lock_irqsave(&priv->lock, flags);
+       /* This is probably racy, but I can't think of
+          a better way, short of rewriting the PCMCIA
+          layer to not suck :-( */
+       if (! test_bit(0, &card->hard_reset_in_progress)) {
+               spin_lock_irqsave(&priv->lock, flags);
 
-                       err = __orinoco_down(dev);
-                       if (err)
-                               printk(KERN_WARNING "%s: Error %d downing interface\n",
-                                      dev->name, err);
+               err = __orinoco_down(dev);
+               if (err)
+                       printk(KERN_WARNING "%s: Error %d downing interface\n",
+                              dev->name, err);
 
-                       netif_device_detach(dev);
-                       priv->hw_unavailable++;
+               netif_device_detach(dev);
+               priv->hw_unavailable++;
 
-                       spin_unlock_irqrestore(&priv->lock, flags);
-               }
-
-               pcmcia_release_configuration(link->handle);
+               spin_unlock_irqrestore(&priv->lock, flags);
        }
 
        return 0;
 }
 
-static int orinoco_cs_resume(struct pcmcia_device *p_dev)
+static int orinoco_cs_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
        struct orinoco_private *priv = netdev_priv(dev);
        struct orinoco_pccard *card = priv->card;
        int err = 0;
        unsigned long flags;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               /* FIXME: should we double check that this is
-                * the same card as we had before */
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-               if (! test_bit(0, &card->hard_reset_in_progress)) {
-                       err = orinoco_reinit_firmware(dev);
-                       if (err) {
-                               printk(KERN_ERR "%s: Error %d re-initializing firmware\n",
-                                      dev->name, err);
-                               return -EIO;
-                       }
-
-                       spin_lock_irqsave(&priv->lock, flags);
+       if (! test_bit(0, &card->hard_reset_in_progress)) {
+               err = orinoco_reinit_firmware(dev);
+               if (err) {
+                       printk(KERN_ERR "%s: Error %d re-initializing firmware\n",
+                              dev->name, err);
+                       return -EIO;
+               }
 
-                       netif_device_attach(dev);
-                       priv->hw_unavailable--;
+               spin_lock_irqsave(&priv->lock, flags);
 
-                       if (priv->open && ! priv->hw_unavailable) {
-                               err = __orinoco_up(dev);
-                               if (err)
-                                       printk(KERN_ERR "%s: Error %d restarting card\n",
-                                              dev->name, err);
-                       }
+               netif_device_attach(dev);
+               priv->hw_unavailable--;
 
-                       spin_unlock_irqrestore(&priv->lock, flags);
+               if (priv->open && ! priv->hw_unavailable) {
+                       err = __orinoco_up(dev);
+                       if (err)
+                               printk(KERN_ERR "%s: Error %d restarting card\n",
+                                      dev->name, err);
                }
+
+               spin_unlock_irqrestore(&priv->lock, flags);
        }
 
        return 0;
@@ -604,7 +565,7 @@ static struct pcmcia_driver orinoco_driver = {
        .drv            = {
                .name   = DRIVER_NAME,
        },
-       .probe          = orinoco_cs_attach,
+       .probe          = orinoco_cs_probe,
        .remove         = orinoco_cs_detach,
        .id_table       = orinoco_cs_ids,
        .suspend        = orinoco_cs_suspend,
index 7880d8c..879eb42 100644 (file)
@@ -90,8 +90,8 @@ module_param(pc_debug, int, 0);
 #define DEBUG(n, args...)
 #endif
 /** Prototypes based on PCMCIA skeleton driver *******************************/
-static void ray_config(dev_link_t *link);
-static void ray_release(dev_link_t *link);
+static int ray_config(struct pcmcia_device *link);
+static void ray_release(struct pcmcia_device *link);
 static void ray_detach(struct pcmcia_device *p_dev);
 
 /***** Prototypes indicated by device structure ******************************/
@@ -190,20 +190,17 @@ static int bc;
 static char *phy_addr = NULL;
 
 
-/* A linked list of "instances" of the ray device.  Each actual
-   PCMCIA card corresponds to one device instance, and is described
-   by one dev_link_t structure (defined in ds.h).
-*/
-static dev_link_t *dev_list = NULL;
-
-/* A dev_link_t structure has fields for most things that are needed
+/* A struct pcmcia_device structure has fields for most things that are needed
    to keep track of a socket, but there will usually be some device
    specific information that also needs to be kept track of.  The
-   'priv' pointer in a dev_link_t structure can be used to point to
+   'priv' pointer in a struct pcmcia_device structure can be used to point to
    a device-specific private data structure, like this.
 */
 static unsigned int ray_mem_speed = 500;
 
+/* WARNING: THIS DRIVER IS NOT CAPABLE OF HANDLING MULTIPLE DEVICES! */
+static struct pcmcia_device *this_device = NULL;
+
 MODULE_AUTHOR("Corey Thomas <corey@world.std.com>");
 MODULE_DESCRIPTION("Raylink/WebGear wireless LAN driver");
 MODULE_LICENSE("GPL");
@@ -306,56 +303,46 @@ static char rcsid[] = "Raylink/WebGear wireless LAN - Corey <Thomas corey@world.
     configure the card at this point -- we wait until we receive a
     card insertion event.
 =============================================================================*/
-static int ray_attach(struct pcmcia_device *p_dev)
+static int ray_probe(struct pcmcia_device *p_dev)
 {
-    dev_link_t *link;
     ray_dev_t *local;
     struct net_device *dev;
-    
-    DEBUG(1, "ray_attach()\n");
 
-    /* Initialize the dev_link_t structure */
-    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
-
-    if (!link)
-           return -ENOMEM;
+    DEBUG(1, "ray_attach()\n");
 
     /* Allocate space for private device-specific data */
     dev = alloc_etherdev(sizeof(ray_dev_t));
-
     if (!dev)
            goto fail_alloc_dev;
 
     local = dev->priv;
-
-    memset(link, 0, sizeof(struct dev_link_t));
+    local->finder = p_dev;
 
     /* The io structure describes IO port mapping. None used here */
-    link->io.NumPorts1 = 0;
-    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-    link->io.IOAddrLines = 5;
+    p_dev->io.NumPorts1 = 0;
+    p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    p_dev->io.IOAddrLines = 5;
 
     /* Interrupt setup. For PCMCIA, driver takes what's given */
-    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
-    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
-    link->irq.Handler = &ray_interrupt;
+    p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+    p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    p_dev->irq.Handler = &ray_interrupt;
 
     /* General socket configuration */
-    link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
-    link->conf.IntType = INT_MEMORY_AND_IO;
-    link->conf.ConfigIndex = 1;
-    link->conf.Present = PRESENT_OPTION;
-
-    link->priv = dev;
-    link->irq.Instance = dev;
+    p_dev->conf.Attributes = CONF_ENABLE_IRQ;
+    p_dev->conf.IntType = INT_MEMORY_AND_IO;
+    p_dev->conf.ConfigIndex = 1;
+    p_dev->conf.Present = PRESENT_OPTION;
+
+    p_dev->priv = dev;
+    p_dev->irq.Instance = dev;
     
-    local->finder = link;
+    local->finder = p_dev;
     local->card_status = CARD_INSERTED;
     local->authentication_state = UNAUTHENTICATED;
     local->num_multi = 0;
-    DEBUG(2,"ray_attach link = %p,  dev = %p,  local = %p, intr = %p\n",
-          link,dev,local,&ray_interrupt);
+    DEBUG(2,"ray_attach p_dev = %p,  dev = %p,  local = %p, intr = %p\n",
+          p_dev,dev,local,&ray_interrupt);
 
     /* Raylink entries in the device structure */
     dev->hard_start_xmit = &ray_dev_start_xmit;
@@ -379,16 +366,10 @@ static int ray_attach(struct pcmcia_device *p_dev)
 
     init_timer(&local->timer);
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    ray_config(link);
-
-    return 0;
+    this_device = p_dev;
+    return ray_config(p_dev);
 
 fail_alloc_dev:
-    kfree(link);
     return -ENOMEM;
 } /* ray_attach */
 /*=============================================================================
@@ -397,37 +378,25 @@ fail_alloc_dev:
     structures are freed.  Otherwise, the structures will be freed
     when the device is released.
 =============================================================================*/
-static void ray_detach(struct pcmcia_device *p_dev)
+static void ray_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-    dev_link_t **linkp;
     struct net_device *dev;
     ray_dev_t *local;
 
     DEBUG(1, "ray_detach(0x%p)\n", link);
-    
-    /* Locate device structure */
-    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
-        if (*linkp == link) break;
-    if (*linkp == NULL)
-        return;
 
+    this_device = NULL;
     dev = link->priv;
 
-    if (link->state & DEV_CONFIG) {
-           ray_release(link);
+    ray_release(link);
 
-           local = (ray_dev_t *)dev->priv;
-            del_timer(&local->timer);
-    }
+    local = (ray_dev_t *)dev->priv;
+    del_timer(&local->timer);
 
-    /* Unlink device structure, free pieces */
-    *linkp = link->next;
     if (link->priv) {
-       if (link->dev) unregister_netdev(dev);
+       if (link->dev_node) unregister_netdev(dev);
         free_netdev(dev);
     }
-    kfree(link);
     DEBUG(2,"ray_cs ray_detach ending\n");
 } /* ray_detach */
 /*=============================================================================
@@ -438,9 +407,8 @@ static void ray_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 #define MAX_TUPLE_SIZE 128
-static void ray_config(dev_link_t *link)
+static int ray_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     tuple_t tuple;
     cisparse_t parse;
     int last_fn = 0, last_ret = 0;
@@ -455,48 +423,45 @@ static void ray_config(dev_link_t *link)
 
     /* This reads the card's CONFIG tuple to find its configuration regs */
     tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     tuple.TupleData = buf;
     tuple.TupleDataMax = MAX_TUPLE_SIZE;
     tuple.TupleOffset = 0;
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present = parse.config.rmask[0];
 
     /* Determine card type and firmware version */
     buf[0] = buf[MAX_TUPLE_SIZE - 1] = 0;
     tuple.DesiredTuple = CISTPL_VERS_1;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     tuple.TupleData = buf;
     tuple.TupleDataMax = MAX_TUPLE_SIZE;
     tuple.TupleOffset = 2;
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
 
     for (i=0; i<tuple.TupleDataLen - 4; i++) 
         if (buf[i] == 0) buf[i] = ' ';
     printk(KERN_INFO "ray_cs Detected: %s\n",buf);
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
     /* Now allocate an interrupt line.  Note that this does not
        actually assign a handler to the interrupt.
     */
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
     dev->irq = link->irq.AssignedIRQ;
     
     /* This actually configures the PCMCIA socket -- setting up
        the I/O windows and the interrupt mapping.
     */
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
 /*** Set up 32k window for shared memory (transmit and control) ************/
     req.Attributes = WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT;
     req.Base = 0;
     req.Size = 0x8000;
     req.AccessSpeed = ray_mem_speed;
-    CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &link->win));
+    CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win));
     mem.CardOffset = 0x0000; mem.Page = 0;
     CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem));
     local->sram = ioremap(req.Base,req.Size);
@@ -506,7 +471,7 @@ static void ray_config(dev_link_t *link)
     req.Base = 0;
     req.Size = 0x4000;
     req.AccessSpeed = ray_mem_speed;
-    CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &local->rmem_handle));
+    CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &local->rmem_handle));
     mem.CardOffset = 0x8000; mem.Page = 0;
     CS_CHECK(MapMemPage, pcmcia_map_mem_page(local->rmem_handle, &mem));
     local->rmem = ioremap(req.Base,req.Size);
@@ -516,7 +481,7 @@ static void ray_config(dev_link_t *link)
     req.Base = 0;
     req.Size = 0x1000;
     req.AccessSpeed = ray_mem_speed;
-    CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &local->amem_handle));
+    CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &local->amem_handle));
     mem.CardOffset = 0x0000; mem.Page = 0;
     CS_CHECK(MapMemPage, pcmcia_map_mem_page(local->amem_handle, &mem));
     local->amem = ioremap(req.Base,req.Size);
@@ -526,32 +491,32 @@ static void ray_config(dev_link_t *link)
     DEBUG(3,"ray_config amem=%p\n",local->amem);
     if (ray_init(dev) < 0) {
         ray_release(link);
-        return;
+        return -ENODEV;
     }
 
-    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+    SET_NETDEV_DEV(dev, &handle_to_dev(link));
     i = register_netdev(dev);
     if (i != 0) {
         printk("ray_config register_netdev() failed\n");
         ray_release(link);
-        return;
+        return i;
     }
 
     strcpy(local->node.dev_name, dev->name);
-    link->dev = &local->node;
+    link->dev_node = &local->node;
 
-    link->state &= ~DEV_CONFIG_PENDING;
     printk(KERN_INFO "%s: RayLink, irq %d, hw_addr ",
        dev->name, dev->irq);
     for (i = 0; i < 6; i++)
     printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
 
-    return;
+    return 0;
 
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 
     ray_release(link);
+    return -ENODEV;
 } /* ray_config */
 
 static inline struct ccs __iomem *ccs_base(ray_dev_t *dev)
@@ -578,9 +543,9 @@ static int ray_init(struct net_device *dev)
     UCHAR *p;
     struct ccs __iomem *pccs;
     ray_dev_t *local = (ray_dev_t *)dev->priv;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
     DEBUG(1, "ray_init(0x%p)\n", dev);
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(0,"ray_init - device not present\n");
         return -1;
     }
@@ -640,10 +605,10 @@ static int dl_startup_params(struct net_device *dev)
     int ccsindex;
     ray_dev_t *local = (ray_dev_t *)dev->priv;
     struct ccs __iomem *pccs;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
 
     DEBUG(1,"dl_startup_params entered\n");
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_cs dl_startup_params - device not present\n");
         return -1;
     }
@@ -747,9 +712,9 @@ static void verify_dl_startup(u_long data)
     ray_dev_t *local = (ray_dev_t *)data;
     struct ccs __iomem *pccs = ccs_base(local) + local->dl_param_ccs;
     UCHAR status;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
 
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_cs verify_dl_startup - device not present\n");
         return;
     }
@@ -787,8 +752,8 @@ static void start_net(u_long data)
     ray_dev_t *local = (ray_dev_t *)data;
     struct ccs __iomem *pccs;
     int ccsindex;
-    dev_link_t *link = local->finder;
-    if (!(link->state & DEV_PRESENT)) {
+    struct pcmcia_device *link = local->finder;
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_cs start_net - device not present\n");
         return;
     }
@@ -814,9 +779,9 @@ static void join_net(u_long data)
 
     struct ccs __iomem *pccs;
     int ccsindex;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
     
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_cs join_net - device not present\n");
         return;
     }
@@ -840,7 +805,7 @@ static void join_net(u_long data)
     device, and release the PCMCIA configuration.  If the device is
     still open, this will be postponed until it is closed.
 =============================================================================*/
-static void ray_release(dev_link_t *link)
+static void ray_release(struct pcmcia_device *link)
 {
     struct net_device *dev = link->priv; 
     ray_dev_t *local = dev->priv;
@@ -849,56 +814,38 @@ static void ray_release(dev_link_t *link)
     DEBUG(1, "ray_release(0x%p)\n", link);
 
     del_timer(&local->timer);
-    link->state &= ~DEV_CONFIG;
 
     iounmap(local->sram);
     iounmap(local->rmem);
     iounmap(local->amem);
     /* Do bother checking to see if these succeed or not */
-    i = pcmcia_release_window(link->win);
-    if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(link->win) ret = %x\n",i);
     i = pcmcia_release_window(local->amem_handle);
     if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(local->amem) ret = %x\n",i);
     i = pcmcia_release_window(local->rmem_handle);
     if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(local->rmem) ret = %x\n",i);
-    i = pcmcia_release_configuration(link->handle);
-    if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseConfiguration ret = %x\n",i);
-    i = pcmcia_release_irq(link->handle, &link->irq);
-    if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseIRQ ret = %x\n",i);
+    pcmcia_disable_device(link);
 
     DEBUG(2,"ray_release ending\n");
 }
 
-static int ray_suspend(struct pcmcia_device *p_dev)
+static int ray_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-        if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-
-               pcmcia_release_configuration(link->handle);
-        }
-
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int ray_resume(struct pcmcia_device *p_dev)
+static int ray_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-        if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       ray_reset(dev);
-                       netif_device_attach(dev);
-               }
-        }
+       if (link->open) {
+               ray_reset(dev);
+               netif_device_attach(dev);
+       }
 
        return 0;
 }
@@ -910,10 +857,10 @@ int ray_dev_init(struct net_device *dev)
     int i;
 #endif /* RAY_IMMEDIATE_INIT */
     ray_dev_t *local = dev->priv;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
 
     DEBUG(1,"ray_dev_init(dev=%p)\n",dev);
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_dev_init - device not present\n");
         return -1;
     }
@@ -944,10 +891,10 @@ int ray_dev_init(struct net_device *dev)
 static int ray_dev_config(struct net_device *dev, struct ifmap *map)
 {
     ray_dev_t *local = dev->priv;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
     /* Dummy routine to satisfy device structure */
     DEBUG(1,"ray_dev_config(dev=%p,ifmap=%p)\n",dev,map);
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_dev_config - device not present\n");
         return -1;
     }
@@ -958,10 +905,10 @@ static int ray_dev_config(struct net_device *dev, struct ifmap *map)
 static int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
     ray_dev_t *local = dev->priv;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
     short length = skb->len;
 
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_dev_start_xmit - device not present\n");
         return -1;
     }
@@ -1570,7 +1517,7 @@ static int ray_commit(struct net_device *dev,
 static iw_stats * ray_get_wireless_stats(struct net_device *   dev)
 {
   ray_dev_t *  local = (ray_dev_t *) dev->priv;
-  dev_link_t *link = local->finder;
+  struct pcmcia_device *link = local->finder;
   struct status __iomem *p = local->sram + STATUS_BASE;
 
   if(local == (ray_dev_t *) NULL)
@@ -1588,7 +1535,7 @@ static iw_stats * ray_get_wireless_stats(struct net_device *      dev)
     }
 #endif /* WIRELESS_SPY */
 
-  if((link->state & DEV_PRESENT)) {
+  if(pcmcia_dev_present(link)) {
     local->wstats.qual.noise = readb(&p->rxnoise);
     local->wstats.qual.updated |= 4;
   }
@@ -1657,18 +1604,14 @@ static const struct iw_handler_def      ray_handler_def =
 /*===========================================================================*/
 static int ray_open(struct net_device *dev)
 {
-    dev_link_t *link;
     ray_dev_t *local = (ray_dev_t *)dev->priv;
+    struct pcmcia_device *link;
+    link = local->finder;
     
     DEBUG(1, "ray_open('%s')\n", dev->name);
 
-    for (link = dev_list; link; link = link->next)
-        if (link->priv == dev) break;
-    if (!DEV_OK(link)) {
-        return -ENODEV;
-    }
-
-    if (link->open == 0) local->num_multi = 0;
+    if (link->open == 0)
+           local->num_multi = 0;
     link->open++;
 
     /* If the card is not started, time to start it ! - Jean II */
@@ -1695,15 +1638,12 @@ static int ray_open(struct net_device *dev)
 /*===========================================================================*/
 static int ray_dev_close(struct net_device *dev)
 {
-    dev_link_t *link;
+    ray_dev_t *local = (ray_dev_t *)dev->priv;
+    struct pcmcia_device *link;
+    link = local->finder;
 
     DEBUG(1, "ray_dev_close('%s')\n", dev->name);
 
-    for (link = dev_list; link; link = link->next)
-        if (link->priv == dev) break;
-    if (link == NULL)
-        return -ENODEV;
-
     link->open--;
     netif_stop_queue(dev);
 
@@ -1725,9 +1665,9 @@ static void ray_reset(struct net_device *dev) {
 static int interrupt_ecf(ray_dev_t *local, int ccs)
 {
     int i = 50;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
 
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_cs interrupt_ecf - device not present\n");
         return -1;
     }
@@ -1752,9 +1692,9 @@ static int get_free_tx_ccs(ray_dev_t *local)
 {
     int i;
     struct ccs __iomem *pccs = ccs_base(local);
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
 
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_cs get_free_tx_ccs - device not present\n");
         return ECARDGONE;
     }
@@ -1783,9 +1723,9 @@ static int get_free_ccs(ray_dev_t *local)
 {
     int i;
     struct ccs __iomem *pccs = ccs_base(local);
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
 
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_cs get_free_ccs - device not present\n");
         return ECARDGONE;
     }
@@ -1858,9 +1798,9 @@ static int parse_addr(char *in_str, UCHAR *out)
 static struct net_device_stats *ray_get_stats(struct net_device *dev)
 {
     ray_dev_t *local = (ray_dev_t *)dev->priv;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
     struct status __iomem *p = local->sram + STATUS_BASE;
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_cs net_device_stats - device not present\n");
         return &local->stats;
     }
@@ -1888,12 +1828,12 @@ static struct net_device_stats *ray_get_stats(struct net_device *dev)
 static void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value, int len)
 {
     ray_dev_t *local = (ray_dev_t *)dev->priv;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
     int ccsindex;
     int i;
     struct ccs __iomem *pccs;
 
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_update_parm - device not present\n");
         return;
     }
@@ -1925,10 +1865,10 @@ static void ray_update_multi_list(struct net_device *dev, int all)
     struct ccs __iomem *pccs;
     int i = 0;
     ray_dev_t *local = (ray_dev_t *)dev->priv;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
     void __iomem *p = local->sram + HOST_TO_ECF_BASE;
 
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_update_multi_list - device not present\n");
         return;
     }
@@ -2005,7 +1945,7 @@ static void set_multicast_list(struct net_device *dev)
 static irqreturn_t ray_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
     struct net_device *dev = (struct net_device *)dev_id;
-    dev_link_t *link;
+    struct pcmcia_device *link;
     ray_dev_t *local;
     struct ccs __iomem *pccs;
     struct rcs __iomem *prcs;
@@ -2020,8 +1960,8 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id, struct pt_regs * regs)
     DEBUG(4,"ray_cs: interrupt for *dev=%p\n",dev);
 
     local = (ray_dev_t *)dev->priv;
-    link = (dev_link_t *)local->finder;
-    if ( ! (link->state & DEV_PRESENT) || link->state & DEV_SUSPEND ) {
+    link = (struct pcmcia_device *)local->finder;
+    if (!pcmcia_dev_present(link)) {
         DEBUG(2,"ray_cs interrupt from device not present or suspended.\n");
         return IRQ_NONE;
     }
@@ -2540,9 +2480,9 @@ static void release_frag_chain(ray_dev_t *local, struct rcs __iomem * prcs)
 /*===========================================================================*/
 static void authenticate(ray_dev_t *local)
 {
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
     DEBUG(0,"ray_cs Starting authentication.\n");
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_cs authenticate - device not present\n");
         return;
     }
@@ -2606,10 +2546,10 @@ static void rx_authenticate(ray_dev_t *local, struct rcs __iomem *prcs,
 static void associate(ray_dev_t *local)
 {
     struct ccs __iomem *pccs;
-    dev_link_t *link = local->finder;
+    struct pcmcia_device *link = local->finder;
     struct net_device *dev = link->priv;
     int ccsindex;
-    if (!(link->state & DEV_PRESENT)) {
+    if (!(pcmcia_dev_present(link))) {
         DEBUG(2,"ray_cs associate - device not present\n");
         return;
     }
@@ -2689,14 +2629,14 @@ static int ray_cs_proc_read(char *buf, char **start, off_t offset, int len)
  * eg ifconfig 
  */
     int i;
-    dev_link_t *link;
+    struct pcmcia_device *link;
     struct net_device *dev;
     ray_dev_t *local;
     UCHAR *p;
     struct freq_hop_element *pfh;
     UCHAR c[33];
 
-    link = dev_list;
+    link = this_device;
     if (!link)
        return 0;
     dev = (struct net_device *)link->priv;
@@ -2898,7 +2838,7 @@ static struct pcmcia_driver ray_driver = {
        .drv            = {
                .name   = "ray_cs",
        },
-       .probe          = ray_attach,
+       .probe          = ray_probe,
        .remove         = ray_detach,
        .id_table       = ray_ids,
        .suspend        = ray_suspend,
@@ -2940,7 +2880,6 @@ static void __exit exit_ray_cs(void)
 #endif
 
     pcmcia_unregister_driver(&ray_driver);
-    BUG_ON(dev_list != NULL);
 } /* exit_ray_cs */
 
 module_init(init_ray_cs);
index 42660fe..bd73ebf 100644 (file)
@@ -31,7 +31,7 @@ typedef struct ray_dev_t {
     void __iomem *sram;            /* pointer to beginning of shared RAM     */
     void __iomem *amem;            /* pointer to attribute mem window        */
     void __iomem *rmem;            /* pointer to receive buffer window       */
-    dev_link_t *finder;            /* pointer back to dev_link_t for card    */
+    struct pcmcia_device *finder;            /* pointer back to struct pcmcia_device for card    */
     struct timer_list timer;
     long tx_ccs_lock;
     long ccs_lock;
index 5fa6fbe..f7b77ce 100644 (file)
@@ -63,7 +63,7 @@ MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket
 /* PCMCIA specific device information (goes in the card field of
  * struct orinoco_private */
 struct orinoco_pccard {
-       dev_link_t link;
+       struct pcmcia_device    *p_dev;
        dev_node_t node;
 };
 
@@ -71,8 +71,8 @@ struct orinoco_pccard {
 /* Function prototypes                                             */
 /********************************************************************/
 
-static void spectrum_cs_config(dev_link_t *link);
-static void spectrum_cs_release(dev_link_t *link);
+static int spectrum_cs_config(struct pcmcia_device *link);
+static void spectrum_cs_release(struct pcmcia_device *link);
 
 /********************************************************************/
 /* Firmware downloader                                             */
@@ -238,14 +238,14 @@ spectrum_aux_open(hermes_t *hw)
  * If IDLE is 1, stop the firmware, so that it can be safely rewritten.
  */
 static int
-spectrum_reset(dev_link_t *link, int idle)
+spectrum_reset(struct pcmcia_device *link, int idle)
 {
        int last_ret, last_fn;
        conf_reg_t reg;
        u_int save_cor;
 
        /* Doing it if hardware is gone is guaranteed crash */
-       if (!(link->state & DEV_CONFIG))
+       if (pcmcia_dev_present(link))
                return -ENODEV;
 
        /* Save original COR value */
@@ -253,7 +253,7 @@ spectrum_reset(dev_link_t *link, int idle)
        reg.Action = CS_READ;
        reg.Offset = CISREG_COR;
        CS_CHECK(AccessConfigurationRegister,
-                pcmcia_access_configuration_register(link->handle, &reg));
+                pcmcia_access_configuration_register(link, &reg));
        save_cor = reg.Value;
 
        /* Soft-Reset card */
@@ -261,14 +261,14 @@ spectrum_reset(dev_link_t *link, int idle)
        reg.Offset = CISREG_COR;
        reg.Value = (save_cor | COR_SOFT_RESET);
        CS_CHECK(AccessConfigurationRegister,
-                pcmcia_access_configuration_register(link->handle, &reg));
+                pcmcia_access_configuration_register(link, &reg));
        udelay(1000);
 
        /* Read CCSR */
        reg.Action = CS_READ;
        reg.Offset = CISREG_CCSR;
        CS_CHECK(AccessConfigurationRegister,
-                pcmcia_access_configuration_register(link->handle, &reg));
+                pcmcia_access_configuration_register(link, &reg));
 
        /*
         * Start or stop the firmware.  Memory width bit should be
@@ -278,7 +278,7 @@ spectrum_reset(dev_link_t *link, int idle)
        reg.Offset = CISREG_CCSR;
        reg.Value = (idle ? HCR_IDLE : HCR_RUN) | (reg.Value & HCR_MEM16);
        CS_CHECK(AccessConfigurationRegister,
-                pcmcia_access_configuration_register(link->handle, &reg));
+                pcmcia_access_configuration_register(link, &reg));
        udelay(1000);
 
        /* Restore original COR configuration index */
@@ -286,12 +286,12 @@ spectrum_reset(dev_link_t *link, int idle)
        reg.Offset = CISREG_COR;
        reg.Value = (save_cor & ~COR_SOFT_RESET);
        CS_CHECK(AccessConfigurationRegister,
-                pcmcia_access_configuration_register(link->handle, &reg));
+                pcmcia_access_configuration_register(link, &reg));
        udelay(1000);
        return 0;
 
       cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
        return -ENODEV;
 }
 
@@ -441,7 +441,7 @@ spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block)
  * care of the PDA - read it and then write it on top of the firmware.
  */
 static int
-spectrum_dl_image(hermes_t *hw, dev_link_t *link,
+spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link,
                  const unsigned char *image)
 {
        int ret;
@@ -505,14 +505,13 @@ spectrum_dl_image(hermes_t *hw, dev_link_t *link,
  * reset on the card, to make sure it's in a sane state.
  */
 static int
-spectrum_dl_firmware(hermes_t *hw, dev_link_t *link)
+spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link)
 {
        int ret;
-       client_handle_t handle = link->handle;
        const struct firmware *fw_entry;
 
        if (request_firmware(&fw_entry, primary_fw_name,
-                            &handle_to_dev(handle)) == 0) {
+                            &handle_to_dev(link)) == 0) {
                primsym = fw_entry->data;
        } else {
                printk(KERN_ERR PFX "Cannot find firmware: %s\n",
@@ -521,7 +520,7 @@ spectrum_dl_firmware(hermes_t *hw, dev_link_t *link)
        }
 
        if (request_firmware(&fw_entry, secondary_fw_name,
-                            &handle_to_dev(handle)) == 0) {
+                            &handle_to_dev(link)) == 0) {
                secsym = fw_entry->data;
        } else {
                printk(KERN_ERR PFX "Cannot find firmware: %s\n",
@@ -554,12 +553,12 @@ static int
 spectrum_cs_hard_reset(struct orinoco_private *priv)
 {
        struct orinoco_pccard *card = priv->card;
-       dev_link_t *link = &card->link;
+       struct pcmcia_device *link = card->p_dev;
        int err;
 
        if (!hermes_present(&priv->hw)) {
                /* The firmware needs to be reloaded */
-               if (spectrum_dl_firmware(&priv->hw, &card->link) != 0) {
+               if (spectrum_dl_firmware(&priv->hw, link) != 0) {
                        printk(KERN_ERR PFX "Firmware download failed\n");
                        err = -ENODEV;
                }
@@ -584,12 +583,11 @@ spectrum_cs_hard_reset(struct orinoco_private *priv)
  * configure the card at this point -- we wait until we receive a card
  * insertion event.  */
 static int
-spectrum_cs_attach(struct pcmcia_device *p_dev)
+spectrum_cs_probe(struct pcmcia_device *link)
 {
        struct net_device *dev;
        struct orinoco_private *priv;
        struct orinoco_pccard *card;
-       dev_link_t *link;
 
        dev = alloc_orinocodev(sizeof(*card), spectrum_cs_hard_reset);
        if (! dev)
@@ -598,7 +596,7 @@ spectrum_cs_attach(struct pcmcia_device *p_dev)
        card = priv->card;
 
        /* Link both structures together */
-       link = &card->link;
+       card->p_dev = link;
        link->priv = dev;
 
        /* Interrupt setup */
@@ -615,13 +613,7 @@ spectrum_cs_attach(struct pcmcia_device *p_dev)
        link->conf.Attributes = 0;
        link->conf.IntType = INT_MEMORY_AND_IO;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       spectrum_cs_config(link);
-
-       return 0;
+       return spectrum_cs_config(link);
 }                              /* spectrum_cs_attach */
 
 /*
@@ -630,16 +622,14 @@ spectrum_cs_attach(struct pcmcia_device *p_dev)
  * are freed.  Otherwise, the structures will be freed when the device
  * is released.
  */
-static void spectrum_cs_detach(struct pcmcia_device *p_dev)
+static void spectrum_cs_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       if (link->state & DEV_CONFIG)
-               spectrum_cs_release(link);
+       spectrum_cs_release(link);
 
-       DEBUG(0, PFX "detach: link=%p link->dev=%p\n", link, link->dev);
-       if (link->dev) {
+       DEBUG(0, PFX "detach: link=%p link->dev_node=%p\n", link, link->dev_node);
+       if (link->dev_node) {
                DEBUG(0, PFX "About to unregister net device %p\n",
                      dev);
                unregister_netdev(dev);
@@ -653,11 +643,10 @@ static void spectrum_cs_detach(struct pcmcia_device *p_dev)
  * device available to the system.
  */
 
-static void
-spectrum_cs_config(dev_link_t *link)
+static int
+spectrum_cs_config(struct pcmcia_device *link)
 {
        struct net_device *dev = link->priv;
-       client_handle_t handle = link->handle;
        struct orinoco_private *priv = netdev_priv(dev);
        struct orinoco_pccard *card = priv->card;
        hermes_t *hw = &priv->hw;
@@ -669,7 +658,7 @@ spectrum_cs_config(dev_link_t *link)
        cisparse_t parse;
        void __iomem *mem;
 
-       CS_CHECK(ValidateCIS, pcmcia_validate_cis(handle, &info));
+       CS_CHECK(ValidateCIS, pcmcia_validate_cis(link, &info));
 
        /*
         * This reads the card's CONFIG tuple to find its
@@ -680,19 +669,15 @@ spectrum_cs_config(dev_link_t *link)
        tuple.TupleData = buf;
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
        /* Look up the current Vcc */
        CS_CHECK(GetConfigurationInfo,
-                pcmcia_get_configuration_info(handle, &conf));
-       link->conf.Vcc = conf.Vcc;
+                pcmcia_get_configuration_info(link, &conf));
 
        /*
         * In this loop, we scan the CIS for configuration table
@@ -709,13 +694,13 @@ spectrum_cs_config(dev_link_t *link)
         * implementation-defined details.
         */
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        while (1) {
                cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
                cistpl_cftable_entry_t dflt = { .index = 0 };
 
-               if ( (pcmcia_get_tuple_data(handle, &tuple) != 0)
-                   || (pcmcia_parse_tuple(handle, &tuple, &parse) != 0))
+               if ( (pcmcia_get_tuple_data(link, &tuple) != 0)
+                   || (pcmcia_parse_tuple(link, &tuple, &parse) != 0))
                        goto next_entry;
 
                if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
@@ -747,10 +732,10 @@ spectrum_cs_config(dev_link_t *link)
                }
 
                if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                            cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
                else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                            dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
                
                /* Do we need to allocate an interrupt? */
@@ -780,7 +765,7 @@ spectrum_cs_config(dev_link_t *link)
                        }
 
                        /* This reserves IO space but doesn't actually enable it */
-                       if (pcmcia_request_io(link->handle, &link->io) != 0)
+                       if (pcmcia_request_io(link, &link->io) != 0)
                                goto next_entry;
                }
 
@@ -790,9 +775,8 @@ spectrum_cs_config(dev_link_t *link)
                break;
                
        next_entry:
-               if (link->io.NumPorts1)
-                       pcmcia_release_io(link->handle, &link->io);
-               last_ret = pcmcia_get_next_tuple(handle, &tuple);
+               pcmcia_disable_device(link);
+               last_ret = pcmcia_get_next_tuple(link, &tuple);
                if (last_ret  == CS_NO_MORE_ITEMS) {
                        printk(KERN_ERR PFX "GetNextTuple(): No matching "
                               "CIS configuration.  Maybe you need the "
@@ -806,7 +790,7 @@ spectrum_cs_config(dev_link_t *link)
         * a handler to the interrupt, unless the 'Handler' member of
         * the irq structure is initialized.
         */
-       CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+       CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
 
        /* We initialize the hermes structure before completing PCMCIA
         * configuration just in case the interrupt handler gets
@@ -823,7 +807,7 @@ spectrum_cs_config(dev_link_t *link)
         * card and host interface into "Memory and IO" mode.
         */
        CS_CHECK(RequestConfiguration,
-                pcmcia_request_configuration(link->handle, &link->conf));
+                pcmcia_request_configuration(link, &link->conf));
 
        /* Ok, we have the configuration, prepare to register the netdev */
        dev->base_addr = link->io.BasePort1;
@@ -836,7 +820,7 @@ spectrum_cs_config(dev_link_t *link)
                goto failed;
        }
 
-       SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+       SET_NETDEV_DEV(dev, &handle_to_dev(link));
        /* Tell the stack we exist */
        if (register_netdev(dev) != 0) {
                printk(KERN_ERR PFX "register_netdev() failed\n");
@@ -844,20 +828,18 @@ spectrum_cs_config(dev_link_t *link)
        }
 
        /* At this point, the dev_node_t structure(s) needs to be
-        * initialized and arranged in a linked list at link->dev. */
+        * initialized and arranged in a linked list at link->dev_node. */
        strcpy(card->node.dev_name, dev->name);
-       link->dev = &card->node; /* link->dev being non-NULL is also
+       link->dev_node = &card->node; /* link->dev_node being non-NULL is also
                                     used to indicate that the
                                     net_device has been registered */
-       link->state &= ~DEV_CONFIG_PENDING;
 
        /* Finally, report what we've done */
-       printk(KERN_DEBUG "%s: index 0x%02x: Vcc %d.%d",
-              dev->name, link->conf.ConfigIndex,
-              link->conf.Vcc / 10, link->conf.Vcc % 10);
-       if (link->conf.Vpp1)
-               printk(", Vpp %d.%d", link->conf.Vpp1 / 10,
-                      link->conf.Vpp1 % 10);
+       printk(KERN_DEBUG "%s: index 0x%02x: ",
+              dev->name, link->conf.ConfigIndex);
+       if (link->conf.Vpp)
+               printk(", Vpp %d.%d", link->conf.Vpp / 10,
+                      link->conf.Vpp % 10);
        printk(", irq %d", link->irq.AssignedIRQ);
        if (link->io.NumPorts1)
                printk(", io 0x%04x-0x%04x", link->io.BasePort1,
@@ -867,13 +849,14 @@ spectrum_cs_config(dev_link_t *link)
                       link->io.BasePort2 + link->io.NumPorts2 - 1);
        printk("\n");
 
-       return;
+       return 0;
 
  cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 
  failed:
        spectrum_cs_release(link);
+       return -ENODEV;
 }                              /* spectrum_cs_config */
 
 /*
@@ -882,7 +865,7 @@ spectrum_cs_config(dev_link_t *link)
  * still open, this will be postponed until it is closed.
  */
 static void
-spectrum_cs_release(dev_link_t *link)
+spectrum_cs_release(struct pcmcia_device *link)
 {
        struct net_device *dev = link->priv;
        struct orinoco_private *priv = netdev_priv(dev);
@@ -894,64 +877,46 @@ spectrum_cs_release(dev_link_t *link)
        priv->hw_unavailable++;
        spin_unlock_irqrestore(&priv->lock, flags);
 
-       /* Don't bother checking to see if these succeed or not */
-       pcmcia_release_configuration(link->handle);
-       if (link->io.NumPorts1)
-               pcmcia_release_io(link->handle, &link->io);
-       if (link->irq.AssignedIRQ)
-               pcmcia_release_irq(link->handle, &link->irq);
-       link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
        if (priv->hw.iobase)
                ioport_unmap(priv->hw.iobase);
 }                              /* spectrum_cs_release */
 
 
 static int
-spectrum_cs_suspend(struct pcmcia_device *p_dev)
+spectrum_cs_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
        struct orinoco_private *priv = netdev_priv(dev);
        unsigned long flags;
        int err = 0;
 
-       link->state |= DEV_SUSPEND;
        /* Mark the device as stopped, to block IO until later */
-       if (link->state & DEV_CONFIG) {
-               spin_lock_irqsave(&priv->lock, flags);
-
-               err = __orinoco_down(dev);
-               if (err)
-                       printk(KERN_WARNING "%s: Error %d downing interface\n",
-                              dev->name, err);
+       spin_lock_irqsave(&priv->lock, flags);
 
-               netif_device_detach(dev);
-               priv->hw_unavailable++;
+       err = __orinoco_down(dev);
+       if (err)
+               printk(KERN_WARNING "%s: Error %d downing interface\n",
+                      dev->name, err);
 
-               spin_unlock_irqrestore(&priv->lock, flags);
+       netif_device_detach(dev);
+       priv->hw_unavailable++;
 
-               pcmcia_release_configuration(link->handle);
-       }
+       spin_unlock_irqrestore(&priv->lock, flags);
 
        return 0;
 }
 
 static int
-spectrum_cs_resume(struct pcmcia_device *p_dev)
+spectrum_cs_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
        struct orinoco_private *priv = netdev_priv(dev);
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               /* FIXME: should we double check that this is
-                * the same card as we had before */
-               pcmcia_request_configuration(link->handle, &link->conf);
-               netif_device_attach(dev);
-               priv->hw_unavailable--;
-               schedule_work(&priv->reset_work);
-       }
+       netif_device_attach(dev);
+       priv->hw_unavailable--;
+       schedule_work(&priv->reset_work);
+
        return 0;
 }
 
@@ -979,7 +944,7 @@ static struct pcmcia_driver orinoco_driver = {
        .drv            = {
                .name   = DRIVER_NAME,
        },
-       .probe          = spectrum_cs_attach,
+       .probe          = spectrum_cs_probe,
        .remove         = spectrum_cs_detach,
        .suspend        = spectrum_cs_suspend,
        .resume         = spectrum_cs_resume,
index 98122f3..f7724eb 100644 (file)
@@ -1005,7 +1005,7 @@ static inline void
 wv_82593_reconfig(struct net_device *  dev)
 {
   net_local *          lp = netdev_priv(dev);
-  dev_link_t *         link = lp->link;
+  struct pcmcia_device *               link = lp->link;
   unsigned long                flags;
 
   /* Arm the flag, will be cleard in wv_82593_config() */
@@ -3744,16 +3744,16 @@ wv_pcmcia_reset(struct net_device *     dev)
 {
   int          i;
   conf_reg_t   reg = { 0, CS_READ, CISREG_COR, 0 };
-  dev_link_t * link = ((net_local *)netdev_priv(dev))->link;
+  struct pcmcia_device *       link = ((net_local *)netdev_priv(dev))->link;
 
 #ifdef DEBUG_CONFIG_TRACE
   printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name);
 #endif
 
-  i = pcmcia_access_configuration_register(link->handle, &reg);
+  i = pcmcia_access_configuration_register(link, &reg);
   if(i != CS_SUCCESS)
     {
-      cs_error(link->handle, AccessConfigurationRegister, i);
+      cs_error(link, AccessConfigurationRegister, i);
       return FALSE;
     }
       
@@ -3764,19 +3764,19 @@ wv_pcmcia_reset(struct net_device *     dev)
 
   reg.Action = CS_WRITE;
   reg.Value = reg.Value | COR_SW_RESET;
-  i = pcmcia_access_configuration_register(link->handle, &reg);
+  i = pcmcia_access_configuration_register(link, &reg);
   if(i != CS_SUCCESS)
     {
-      cs_error(link->handle, AccessConfigurationRegister, i);
+      cs_error(link, AccessConfigurationRegister, i);
       return FALSE;
     }
       
   reg.Action = CS_WRITE;
   reg.Value = COR_LEVEL_IRQ | COR_CONFIG;
-  i = pcmcia_access_configuration_register(link->handle, &reg);
+  i = pcmcia_access_configuration_register(link, &reg);
   if(i != CS_SUCCESS)
     {
-      cs_error(link->handle, AccessConfigurationRegister, i);
+      cs_error(link, AccessConfigurationRegister, i);
       return FALSE;
     }
 
@@ -3940,9 +3940,8 @@ wv_hw_reset(struct net_device *   dev)
  * (called by wavelan_event())
  */
 static inline int
-wv_pcmcia_config(dev_link_t *  link)
+wv_pcmcia_config(struct pcmcia_device *        link)
 {
-  client_handle_t      handle = link->handle;
   tuple_t              tuple;
   cisparse_t           parse;
   struct net_device *  dev = (struct net_device *) link->priv;
@@ -3965,16 +3964,16 @@ wv_pcmcia_config(dev_link_t *   link)
     {
       tuple.Attributes = 0;
       tuple.DesiredTuple = CISTPL_CONFIG;
-      i = pcmcia_get_first_tuple(handle, &tuple);
+      i = pcmcia_get_first_tuple(link, &tuple);
       if(i != CS_SUCCESS)
        break;
       tuple.TupleData = (cisdata_t *)buf;
       tuple.TupleDataMax = 64;
       tuple.TupleOffset = 0;
-      i = pcmcia_get_tuple_data(handle, &tuple);
+      i = pcmcia_get_tuple_data(link, &tuple);
       if(i != CS_SUCCESS)
        break;
-      i = pcmcia_parse_tuple(handle, &tuple, &parse);
+      i = pcmcia_parse_tuple(link, &tuple, &parse);
       if(i != CS_SUCCESS)
        break;
       link->conf.ConfigBase = parse.config.base;
@@ -3983,19 +3982,16 @@ wv_pcmcia_config(dev_link_t *   link)
   while(0);
   if(i != CS_SUCCESS)
     {
-      cs_error(link->handle, ParseTuple, i);
-      link->state &= ~DEV_CONFIG_PENDING;
+      cs_error(link, ParseTuple, i);
       return FALSE;
     }
-    
-  /* Configure card */
-  link->state |= DEV_CONFIG;
+
   do
     {
-      i = pcmcia_request_io(link->handle, &link->io);
+      i = pcmcia_request_io(link, &link->io);
       if(i != CS_SUCCESS)
        {
-         cs_error(link->handle, RequestIO, i);
+         cs_error(link, RequestIO, i);
          break;
        }
 
@@ -4003,10 +3999,10 @@ wv_pcmcia_config(dev_link_t *   link)
        * Now allocate an interrupt line.  Note that this does not
        * actually assign a handler to the interrupt.
        */
-      i = pcmcia_request_irq(link->handle, &link->irq);
+      i = pcmcia_request_irq(link, &link->irq);
       if(i != CS_SUCCESS)
        {
-         cs_error(link->handle, RequestIRQ, i);
+         cs_error(link, RequestIRQ, i);
          break;
        }
 
@@ -4015,15 +4011,15 @@ wv_pcmcia_config(dev_link_t *   link)
        * the I/O windows and the interrupt mapping.
        */
       link->conf.ConfigIndex = 1;
-      i = pcmcia_request_configuration(link->handle, &link->conf);
+      i = pcmcia_request_configuration(link, &link->conf);
       if(i != CS_SUCCESS)
        {
-         cs_error(link->handle, RequestConfiguration, i);
+         cs_error(link, RequestConfiguration, i);
          break;
        }
 
       /*
-       * Allocate a small memory window.  Note that the dev_link_t
+       * Allocate a small memory window.  Note that the struct pcmcia_device
        * structure provides space for one window handle -- if your
        * device needs several windows, you'll need to keep track of
        * the handles in your private data structure, link->priv.
@@ -4031,10 +4027,10 @@ wv_pcmcia_config(dev_link_t *   link)
       req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
       req.Base = req.Size = 0;
       req.AccessSpeed = mem_speed;
-      i = pcmcia_request_window(&link->handle, &req, &link->win);
+      i = pcmcia_request_window(&link, &req, &link->win);
       if(i != CS_SUCCESS)
        {
-         cs_error(link->handle, RequestWindow, i);
+         cs_error(link, RequestWindow, i);
          break;
        }
 
@@ -4046,7 +4042,7 @@ wv_pcmcia_config(dev_link_t *     link)
       i = pcmcia_map_mem_page(link->win, &mem);
       if(i != CS_SUCCESS)
        {
-         cs_error(link->handle, MapMemPage, i);
+         cs_error(link, MapMemPage, i);
          break;
        }
 
@@ -4060,7 +4056,7 @@ wv_pcmcia_config(dev_link_t *     link)
             lp->mem, dev->irq, (u_int) dev->base_addr);
 #endif
 
-      SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+      SET_NETDEV_DEV(dev, &handle_to_dev(link));
       i = register_netdev(dev);
       if(i != 0)
        {
@@ -4072,7 +4068,6 @@ wv_pcmcia_config(dev_link_t *     link)
     }
   while(0);            /* Humm... Disguised goto !!! */
 
-  link->state &= ~DEV_CONFIG_PENDING;
   /* If any step failed, release any partially configured state */
   if(i != 0)
     {
@@ -4081,7 +4076,7 @@ wv_pcmcia_config(dev_link_t *     link)
     }
 
   strcpy(((net_local *) netdev_priv(dev))->node.dev_name, dev->name);
-  link->dev = &((net_local *) netdev_priv(dev))->node;
+  link->dev_node = &((net_local *) netdev_priv(dev))->node;
 
 #ifdef DEBUG_CONFIG_TRACE
   printk(KERN_DEBUG "<-wv_pcmcia_config()\n");
@@ -4096,26 +4091,20 @@ wv_pcmcia_config(dev_link_t *   link)
  * still open, this will be postponed until it is closed.
  */
 static void
-wv_pcmcia_release(dev_link_t *link)
+wv_pcmcia_release(struct pcmcia_device *link)
 {
-  struct net_device *  dev = (struct net_device *) link->priv;
-  net_local *          lp = netdev_priv(dev);
+       struct net_device *     dev = (struct net_device *) link->priv;
+       net_local *             lp = netdev_priv(dev);
 
 #ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link);
+       printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link);
 #endif
 
-  /* Don't bother checking to see if these succeed or not */
-  iounmap(lp->mem);
-  pcmcia_release_window(link->win);
-  pcmcia_release_configuration(link->handle);
-  pcmcia_release_io(link->handle, &link->io);
-  pcmcia_release_irq(link->handle, &link->irq);
-
-  link->state &= ~DEV_CONFIG;
+       iounmap(lp->mem);
+       pcmcia_disable_device(link);
 
 #ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name);
+       printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name);
 #endif
 }
 
@@ -4479,7 +4468,7 @@ static int
 wavelan_open(struct net_device *       dev)
 {
   net_local *  lp = netdev_priv(dev);
-  dev_link_t * link = lp->link;
+  struct pcmcia_device *       link = lp->link;
   kio_addr_t   base = dev->base_addr;
 
 #ifdef DEBUG_CALLBACK_TRACE
@@ -4533,7 +4522,7 @@ wavelan_open(struct net_device *  dev)
 static int
 wavelan_close(struct net_device *      dev)
 {
-  dev_link_t * link = ((net_local *)netdev_priv(dev))->link;
+  struct pcmcia_device *       link = ((net_local *)netdev_priv(dev))->link;
   kio_addr_t   base = dev->base_addr;
 
 #ifdef DEBUG_CALLBACK_TRACE
@@ -4587,45 +4576,36 @@ wavelan_close(struct net_device *       dev)
  * card insertion event.
  */
 static int
-wavelan_attach(struct pcmcia_device *p_dev)
+wavelan_probe(struct pcmcia_device *p_dev)
 {
-  dev_link_t * link;           /* Info for cardmgr */
   struct net_device *  dev;            /* Interface generic data */
   net_local *  lp;             /* Interface specific data */
+  int ret;
 
 #ifdef DEBUG_CALLBACK_TRACE
   printk(KERN_DEBUG "-> wavelan_attach()\n");
 #endif
 
-  /* Initialize the dev_link_t structure */
-  link = kzalloc(sizeof(struct dev_link_t), GFP_KERNEL);
-  if (!link) return -ENOMEM;
-
   /* The io structure describes IO port mapping */
-  link->io.NumPorts1 = 8;
-  link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-  link->io.IOAddrLines = 3;
+  p_dev->io.NumPorts1 = 8;
+  p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+  p_dev->io.IOAddrLines = 3;
 
   /* Interrupt setup */
-  link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
-  link->irq.IRQInfo1 = IRQ_LEVEL_ID;
-  link->irq.Handler = wavelan_interrupt;
+  p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+  p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+  p_dev->irq.Handler = wavelan_interrupt;
 
   /* General socket configuration */
-  link->conf.Attributes = CONF_ENABLE_IRQ;
-  link->conf.Vcc = 50;
-  link->conf.IntType = INT_MEMORY_AND_IO;
-
-  /* Chain drivers */
-  link->next = NULL;
+  p_dev->conf.Attributes = CONF_ENABLE_IRQ;
+  p_dev->conf.IntType = INT_MEMORY_AND_IO;
 
   /* Allocate the generic data structure */
   dev = alloc_etherdev(sizeof(net_local));
-  if (!dev) {
-      kfree(link);
+  if (!dev)
       return -ENOMEM;
-  }
-  link->priv = link->irq.Instance = dev;
+
+  p_dev->priv = p_dev->irq.Instance = dev;
 
   lp = netdev_priv(dev);
 
@@ -4642,7 +4622,6 @@ wavelan_attach(struct pcmcia_device *p_dev)
   spin_lock_init(&lp->spinlock);
 
   /* back links */
-  lp->link = link;
   lp->dev = dev;
 
   /* wavelan NET3 callbacks */
@@ -4668,15 +4647,18 @@ wavelan_attach(struct pcmcia_device *p_dev)
   /* Other specific data */
   dev->mtu = WAVELAN_MTU;
 
-  link->handle = p_dev;
-  p_dev->instance = link;
+  ret = wv_pcmcia_config(p_dev);
+  if (ret)
+         return ret;
 
-  link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-  if(wv_pcmcia_config(link) &&
-     wv_hw_config(dev))
-         wv_init_info(dev);
-  else
+  ret = wv_hw_config(dev);
+  if (ret) {
          dev->irq = 0;
+         pcmcia_disable_device(p_dev);
+         return ret;
+  }
+
+  wv_init_info(dev);
 
 #ifdef DEBUG_CALLBACK_TRACE
   printk(KERN_DEBUG "<- wavelan_attach()\n");
@@ -4693,25 +4675,14 @@ wavelan_attach(struct pcmcia_device *p_dev)
  * is released.
  */
 static void
-wavelan_detach(struct pcmcia_device *p_dev)
+wavelan_detach(struct pcmcia_device *link)
 {
-   dev_link_t *link = dev_to_instance(p_dev);
-
 #ifdef DEBUG_CALLBACK_TRACE
   printk(KERN_DEBUG "-> wavelan_detach(0x%p)\n", link);
 #endif
 
-  /*
-   * If the device is currently configured and active, we won't
-   * actually delete it yet.  Instead, it is marked so that when the
-   * release() function is called, that will trigger a proper
-   * detach().
-   */
-  if(link->state & DEV_CONFIG)
-    {
-      /* Some others haven't done their job : give them another chance */
-      wv_pcmcia_release(link);
-    }
+  /* Some others haven't done their job : give them another chance */
+  wv_pcmcia_release(link);
 
   /* Free pieces */
   if(link->priv)
@@ -4720,23 +4691,21 @@ wavelan_detach(struct pcmcia_device *p_dev)
 
       /* Remove ourselves from the kernel list of ethernet devices */
       /* Warning : can't be called from interrupt, timer or wavelan_close() */
-      if (link->dev)
+      if (link->dev_node)
        unregister_netdev(dev);
-      link->dev = NULL;
+      link->dev_node = NULL;
       ((net_local *)netdev_priv(dev))->link = NULL;
       ((net_local *)netdev_priv(dev))->dev = NULL;
       free_netdev(dev);
     }
-  kfree(link);
 
 #ifdef DEBUG_CALLBACK_TRACE
   printk(KERN_DEBUG "<- wavelan_detach()\n");
 #endif
 }
 
-static int wavelan_suspend(struct pcmcia_device *p_dev)
+static int wavelan_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *     dev = (struct net_device *) link->priv;
 
        /* NB: wavelan_close will be called, but too late, so we are
@@ -4748,36 +4717,22 @@ static int wavelan_suspend(struct pcmcia_device *p_dev)
        /* Stop receiving new messages and wait end of transmission */
        wv_ru_stop(dev);
 
+       if (link->open)
+               netif_device_detach(dev);
+
        /* Power down the module */
        hacr_write(dev->base_addr, HACR_DEFAULT & (~HACR_PWR_STAT));
 
-       /* The card is now suspended */
-       link->state |= DEV_SUSPEND;
-
-       if(link->state & DEV_CONFIG)
-       {
-               if(link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-       }
-
        return 0;
 }
 
-static int wavelan_resume(struct pcmcia_device *p_dev)
+static int wavelan_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *     dev = (struct net_device *) link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if(link->state & DEV_CONFIG)
-       {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if(link->open)  /* If RESET -> True, If RESUME -> False ? */
-               {
-                       wv_hw_reset(dev);
-                       netif_device_attach(dev);
-               }
+       if (link->open) {
+               wv_hw_reset(dev);
+               netif_device_attach(dev);
        }
 
        return 0;
@@ -4798,7 +4753,7 @@ static struct pcmcia_driver wavelan_driver = {
        .drv            = {
                .name   = "wavelan_cs",
        },
-       .probe          = wavelan_attach,
+       .probe          = wavelan_probe,
        .remove         = wavelan_detach,
        .id_table       = wavelan_ids,
        .suspend        = wavelan_suspend,
index 451f627..c65fe7a 100644 (file)
@@ -602,7 +602,7 @@ struct net_local
   dev_node_t   node;           /* ???? What is this stuff ???? */
   struct net_device *  dev;            /* Reverse link... */
   spinlock_t   spinlock;       /* Serialize access to the hardware (SMP) */
-  dev_link_t * link;           /* pcmcia structure */
+  struct pcmcia_device *       link;           /* pcmcia structure */
   en_stats     stats;          /* Ethernet interface statistics */
   int          nresets;        /* Number of hw resets */
   u_char       configured;     /* If it is configured */
@@ -733,9 +733,9 @@ static int
 static inline void
        wv_hw_reset(struct net_device *);       /* Same, + start receiver unit */
 static inline int
-       wv_pcmcia_config(dev_link_t *); /* Configure the pcmcia interface */
+       wv_pcmcia_config(struct pcmcia_device *);       /* Configure the pcmcia interface */
 static void
-       wv_pcmcia_release(dev_link_t *);/* Remove a device */
+       wv_pcmcia_release(struct pcmcia_device *);/* Remove a device */
 /* ---------------------- INTERRUPT HANDLING ---------------------- */
 static irqreturn_t
        wavelan_interrupt(int,  /* Interrupt handler */
index 4303c50..65ceb08 100644 (file)
@@ -611,5 +611,6 @@ struct wl3501_card {
        struct iw_spy_data              spy_data;
        struct iw_public_data           wireless_data;
        struct dev_node_t               node;
+       struct pcmcia_device            *p_dev;
 };
 #endif
index 48e10b0..e52a650 100644 (file)
@@ -103,8 +103,8 @@ module_param(pc_debug, int, 0);
  * release a socket, in response to card insertion and ejection events.  They
  * are invoked from the wl24 event handler.
  */
-static void wl3501_config(dev_link_t *link);
-static void wl3501_release(dev_link_t *link);
+static int wl3501_config(struct pcmcia_device *link);
+static void wl3501_release(struct pcmcia_device *link);
 
 /*
  * The dev_info variable is the "key" that is used to match up this
@@ -226,17 +226,6 @@ static void iw_copy_mgmt_info_element(struct iw_mgmt_info_element *to,
        iw_set_mgmt_info_element(from->id, to, from->data, from->len);
 }
 
-/*
- * A linked list of "instances" of the wl24 device.  Each actual PCMCIA card
- * corresponds to one device instance, and is described by one dev_link_t
- * structure (defined in ds.h).
- *
- * You may not want to use a linked list for this -- for example, the memory
- * card driver uses an array of dev_link_t pointers, where minor device numbers
- * are used to derive the corresponding array index.
- */
-static dev_link_t *wl3501_dev_list;
-
 static inline void wl3501_switch_page(struct wl3501_card *this, u8 page)
 {
        wl3501_outb(page, this->base_addr + WL3501_NIC_BSS);
@@ -1281,15 +1270,10 @@ static int wl3501_close(struct net_device *dev)
        struct wl3501_card *this = dev->priv;
        int rc = -ENODEV;
        unsigned long flags;
-       dev_link_t *link;
+       struct pcmcia_device *link;
+       link = this->p_dev;
 
        spin_lock_irqsave(&this->lock, flags);
-       /* Check if the device is in wl3501_dev_list */
-       for (link = wl3501_dev_list; link; link = link->next)
-               if (link->priv == dev)
-                       break;
-       if (!link)
-               goto out;
        link->open--;
 
        /* Stop wl3501_hard_start_xmit() from now on */
@@ -1301,7 +1285,6 @@ static int wl3501_close(struct net_device *dev)
 
        rc = 0;
        printk(KERN_INFO "%s: WL3501 closed\n", dev->name);
-out:
        spin_unlock_irqrestore(&this->lock, flags);
        return rc;
 }
@@ -1400,14 +1383,11 @@ static int wl3501_open(struct net_device *dev)
        int rc = -ENODEV;
        struct wl3501_card *this = dev->priv;
        unsigned long flags;
-       dev_link_t *link;
+       struct pcmcia_device *link;
+       link = this->p_dev;
 
        spin_lock_irqsave(&this->lock, flags);
-       /* Check if the device is in wl3501_dev_list */
-       for (link = wl3501_dev_list; link; link = link->next)
-               if (link->priv == dev)
-                       break;
-       if (!DEV_OK(link))
+       if (!pcmcia_dev_present(link))
                goto out;
        netif_device_attach(dev);
        link->open++;
@@ -1497,38 +1477,23 @@ static struct ethtool_ops ops = {
  * Services. If it has been released, all local data structures are freed.
  * Otherwise, the structures will be freed when the device is released.
  */
-static void wl3501_detach(struct pcmcia_device *p_dev)
+static void wl3501_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
-       dev_link_t **linkp;
        struct net_device *dev = link->priv;
 
-       /* Locate device structure */
-       for (linkp = &wl3501_dev_list; *linkp; linkp = &(*linkp)->next)
-               if (*linkp == link)
-                       break;
-       if (!*linkp)
-               goto out;
-
        /* If the device is currently configured and active, we won't actually
         * delete it yet.  Instead, it is marked so that when the release()
         * function is called, that will trigger a proper detach(). */
 
-       if (link->state & DEV_CONFIG) {
-               while (link->open > 0)
-                       wl3501_close(dev);
-
-               netif_device_detach(dev);
-               wl3501_release(link);
-       }
+       while (link->open > 0)
+               wl3501_close(dev);
 
-       /* Unlink device structure, free pieces */
-       *linkp = link->next;
+       netif_device_detach(dev);
+       wl3501_release(link);
 
        if (link->priv)
                free_netdev(link->priv);
-       kfree(link);
-out:
+
        return;
 }
 
@@ -1953,33 +1918,26 @@ static const struct iw_handler_def wl3501_handler_def = {
  * The dev_link structure is initialized, but we don't actually configure the
  * card at this point -- we wait until we receive a card insertion event.
  */
-static int wl3501_attach(struct pcmcia_device *p_dev)
+static int wl3501_probe(struct pcmcia_device *p_dev)
 {
-       dev_link_t *link;
        struct net_device *dev;
        struct wl3501_card *this;
 
-       /* Initialize the dev_link_t structure */
-       link = kzalloc(sizeof(*link), GFP_KERNEL);
-       if (!link)
-               return -ENOMEM;
-
        /* The io structure describes IO port mapping */
-       link->io.NumPorts1      = 16;
-       link->io.Attributes1    = IO_DATA_PATH_WIDTH_8;
-       link->io.IOAddrLines    = 5;
+       p_dev->io.NumPorts1     = 16;
+       p_dev->io.Attributes1   = IO_DATA_PATH_WIDTH_8;
+       p_dev->io.IOAddrLines   = 5;
 
        /* Interrupt setup */
-       link->irq.Attributes    = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
-       link->irq.IRQInfo1      = IRQ_LEVEL_ID;
-       link->irq.Handler = wl3501_interrupt;
+       p_dev->irq.Attributes   = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+       p_dev->irq.IRQInfo1     = IRQ_LEVEL_ID;
+       p_dev->irq.Handler = wl3501_interrupt;
 
        /* General socket configuration */
-       link->conf.Attributes   = CONF_ENABLE_IRQ;
-       link->conf.Vcc          = 50;
-       link->conf.IntType      = INT_MEMORY_AND_IO;
-       link->conf.ConfigIndex  = 1;
-       link->conf.Present      = PRESENT_OPTION;
+       p_dev->conf.Attributes  = CONF_ENABLE_IRQ;
+       p_dev->conf.IntType     = INT_MEMORY_AND_IO;
+       p_dev->conf.ConfigIndex = 1;
+       p_dev->conf.Present     = PRESENT_OPTION;
 
        dev = alloc_etherdev(sizeof(struct wl3501_card));
        if (!dev)
@@ -1992,22 +1950,15 @@ static int wl3501_attach(struct pcmcia_device *p_dev)
        dev->get_stats          = wl3501_get_stats;
        this = dev->priv;
        this->wireless_data.spy_data = &this->spy_data;
+       this->p_dev = p_dev;
        dev->wireless_data      = &this->wireless_data;
        dev->wireless_handlers  = (struct iw_handler_def *)&wl3501_handler_def;
        SET_ETHTOOL_OPS(dev, &ops);
        netif_stop_queue(dev);
-       link->priv = link->irq.Instance = dev;
-
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       wl3501_config(link);
+       p_dev->priv = p_dev->irq.Instance = dev;
 
-       return 0;
+       return wl3501_config(p_dev);
 out_link:
-       kfree(link);
-       link = NULL;
        return -ENOMEM;
 }
 
@@ -2022,11 +1973,10 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
  * received, to configure the PCMCIA socket, and to make the ethernet device
  * available to the system.
  */
-static void wl3501_config(dev_link_t *link)
+static int wl3501_config(struct pcmcia_device *link)
 {
        tuple_t tuple;
        cisparse_t parse;
-       client_handle_t handle = link->handle;
        struct net_device *dev = link->priv;
        int i = 0, j, last_fn, last_ret;
        unsigned char bf[64];
@@ -2035,18 +1985,15 @@ static void wl3501_config(dev_link_t *link)
        /* This reads the card's CONFIG tuple to find its config registers. */
        tuple.Attributes        = 0;
        tuple.DesiredTuple      = CISTPL_CONFIG;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        tuple.TupleData         = bf;
        tuple.TupleDataMax      = sizeof(bf);
        tuple.TupleOffset       = 0;
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase   = parse.config.base;
        link->conf.Present      = parse.config.rmask[0];
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
        /* Try allocating IO ports.  This tries a few fixed addresses.  If you
         * want, you can also read the card's config table to pick addresses --
         * see the serial driver for an example. */
@@ -2056,28 +2003,28 @@ static void wl3501_config(dev_link_t *link)
                 * 0x200-0x2ff, and so on, because this seems safer */
                link->io.BasePort1 = j;
                link->io.BasePort2 = link->io.BasePort1 + 0x10;
-               i = pcmcia_request_io(link->handle, &link->io);
+               i = pcmcia_request_io(link, &link->io);
                if (i == CS_SUCCESS)
                        break;
        }
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestIO, i);
+               cs_error(link, RequestIO, i);
                goto failed;
        }
 
        /* Now allocate an interrupt line. Note that this does not actually
         * assign a handler to the interrupt. */
 
-       CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+       CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
 
        /* This actually configures the PCMCIA socket -- setting up the I/O
         * windows and the interrupt mapping.  */
 
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
        dev->irq = link->irq.AssignedIRQ;
        dev->base_addr = link->io.BasePort1;
-       SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+       SET_NETDEV_DEV(dev, &handle_to_dev(link));
        if (register_netdev(dev)) {
                printk(KERN_NOTICE "wl3501_cs: register_netdev() failed\n");
                goto failed;
@@ -2088,10 +2035,9 @@ static void wl3501_config(dev_link_t *link)
        this = dev->priv;
        /*
         * At this point, the dev_node_t structure(s) should be initialized and
-        * arranged in a linked list at link->dev.
+        * arranged in a linked list at link->dev_node.
         */
-       link->dev = &this->node;
-       link->state &= ~DEV_CONFIG_PENDING;
+       link->dev_node = &this->node;
 
        this->base_addr = dev->base_addr;
 
@@ -2127,13 +2073,13 @@ static void wl3501_config(dev_link_t *link)
        spin_lock_init(&this->lock);
        init_waitqueue_head(&this->wait);
        netif_start_queue(dev);
-       goto out;
+       return 0;
+
 cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 failed:
        wl3501_release(link);
-out:
-       return;
+       return -ENODEV;
 }
 
 /**
@@ -2144,52 +2090,36 @@ out:
  * and release the PCMCIA configuration.  If the device is still open, this
  * will be postponed until it is closed.
  */
-static void wl3501_release(dev_link_t *link)
+static void wl3501_release(struct pcmcia_device *link)
 {
        struct net_device *dev = link->priv;
 
        /* Unlink the device chain */
-       if (link->dev) {
+       if (link->dev_node)
                unregister_netdev(dev);
-               link->dev = NULL;
-       }
 
-       /* Don't bother checking to see if these succeed or not */
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
-       link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
 }
 
-static int wl3501_suspend(struct pcmcia_device *p_dev)
+static int wl3501_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
-       link->state |= DEV_SUSPEND;
-
        wl3501_pwr_mgmt(dev->priv, WL3501_SUSPEND);
-       if (link->state & DEV_CONFIG) {
-               if (link->open)
-                       netif_device_detach(dev);
-               pcmcia_release_configuration(link->handle);
-       }
+       if (link->open)
+               netif_device_detach(dev);
 
        return 0;
 }
 
-static int wl3501_resume(struct pcmcia_device *p_dev)
+static int wl3501_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct net_device *dev = link->priv;
 
        wl3501_pwr_mgmt(dev->priv, WL3501_RESUME);
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if (link->open) {
-                       wl3501_reset(dev);
-                       netif_device_attach(dev);
-               }
+       if (link->open) {
+               wl3501_reset(dev);
+               netif_device_attach(dev);
        }
 
        return 0;
@@ -2207,7 +2137,7 @@ static struct pcmcia_driver wl3501_driver = {
        .drv            = {
                .name   = "wl3501_cs",
        },
-       .probe          = wl3501_attach,
+       .probe          = wl3501_probe,
        .remove         = wl3501_detach,
        .id_table       = wl3501_ids,
        .suspend        = wl3501_suspend,
@@ -2221,9 +2151,7 @@ static int __init wl3501_init_module(void)
 
 static void __exit wl3501_exit_module(void)
 {
-       dprintk(0, ": unloading");
        pcmcia_unregister_driver(&wl3501_driver);
-       BUG_ON(wl3501_dev_list != NULL);
 }
 
 module_init(wl3501_init_module);
index 75d56bf..fd0f43b 100644 (file)
@@ -1441,8 +1441,7 @@ static void __devexit yellowfin_remove_one (struct pci_dev *pdev)
        struct net_device *dev = pci_get_drvdata(pdev);
        struct yellowfin_private *np;
 
-       if (!dev)
-               BUG();
+       BUG_ON(!dev);
        np = netdev_priv(dev);
 
         pci_free_consistent(pdev, STATUS_TOTAL_SIZE, np->tx_status, 
index 158d925..b953d59 100644 (file)
@@ -81,15 +81,15 @@ static char *version =
 #define FORCE_EPP_MODE 0x08
 
 typedef struct parport_info_t {
-    dev_link_t         link;
+       struct pcmcia_device    *p_dev;
     int                        ndev;
     dev_node_t         node;
     struct parport     *port;
 } parport_info_t;
 
 static void parport_detach(struct pcmcia_device *p_dev);
-static void parport_config(dev_link_t *link);
-static void parport_cs_release(dev_link_t *);
+static int parport_config(struct pcmcia_device *link);
+static void parport_cs_release(struct pcmcia_device *);
 
 /*======================================================================
 
@@ -99,10 +99,9 @@ static void parport_cs_release(dev_link_t *);
 
 ======================================================================*/
 
-static int parport_attach(struct pcmcia_device *p_dev)
+static int parport_probe(struct pcmcia_device *link)
 {
     parport_info_t *info;
-    dev_link_t *link;
 
     DEBUG(0, "parport_attach()\n");
 
@@ -110,23 +109,17 @@ static int parport_attach(struct pcmcia_device *p_dev)
     info = kmalloc(sizeof(*info), GFP_KERNEL);
     if (!info) return -ENOMEM;
     memset(info, 0, sizeof(*info));
-    link = &info->link; link->priv = info;
+    link->priv = info;
+    info->p_dev = link;
 
     link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
     link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
     link->irq.IRQInfo1 = IRQ_LEVEL_ID;
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    parport_config(link);
-
-    return 0;
+    return parport_config(link);
 } /* parport_attach */
 
 /*======================================================================
@@ -138,14 +131,11 @@ static int parport_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void parport_detach(struct pcmcia_device *p_dev)
+static void parport_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-
     DEBUG(0, "parport_detach(0x%p)\n", link);
 
-    if (link->state & DEV_CONFIG)
-       parport_cs_release(link);
+    parport_cs_release(link);
 
     kfree(link->priv);
 } /* parport_detach */
@@ -161,14 +151,12 @@ static void parport_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-void parport_config(dev_link_t *link)
+static int parport_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     parport_info_t *info = link->priv;
     tuple_t tuple;
     u_short buf[128];
     cisparse_t parse;
-    config_info_t conf;
     cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
     cistpl_cftable_entry_t dflt = { 0 };
     struct parport *p;
@@ -180,24 +168,18 @@ void parport_config(dev_link_t *link)
     tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
     tuple.Attributes = 0;
     tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present = parse.config.rmask[0];
-    
-    /* Configure card */
-    link->state |= DEV_CONFIG;
 
-    /* Not sure if this is right... look up the current Vcc */
-    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
-    
     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
     tuple.Attributes = 0;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     while (1) {
-       if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+       if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+               pcmcia_parse_tuple(link, &tuple, &parse) != 0)
            goto next_entry;
 
        if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
@@ -212,7 +194,7 @@ void parport_config(dev_link_t *link)
                link->io.BasePort2 = io->win[1].base;
                link->io.NumPorts2 = io->win[1].len;
            }
-           if (pcmcia_request_io(link->handle, &link->io) != 0)
+           if (pcmcia_request_io(link, &link->io) != 0)
                goto next_entry;
            /* If we've got this far, we're done */
            break;
@@ -220,15 +202,12 @@ void parport_config(dev_link_t *link)
        
     next_entry:
        if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
-       CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+       CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
     }
     
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
-    release_region(link->io.BasePort1, link->io.NumPorts1);
-    if (link->io.NumPorts2)
-       release_region(link->io.BasePort2, link->io.NumPorts2);
     p = parport_pc_probe_port(link->io.BasePort1, link->io.BasePort2,
                              link->irq.AssignedIRQ, PARPORT_DMA_NONE,
                              NULL);
@@ -247,17 +226,15 @@ void parport_config(dev_link_t *link)
     info->node.minor = p->number;
     info->port = p;
     strcpy(info->node.dev_name, p->name);
-    link->dev = &info->node;
+    link->dev_node = &info->node;
+
+    return 0;
 
-    link->state &= ~DEV_CONFIG_PENDING;
-    return;
-    
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
 failed:
     parport_cs_release(link);
-    link->state &= ~DEV_CONFIG_PENDING;
-
+    return -ENODEV;
 } /* parport_config */
 
 /*======================================================================
@@ -268,53 +245,21 @@ failed:
     
 ======================================================================*/
 
-void parport_cs_release(dev_link_t *link)
-{
-    parport_info_t *info = link->priv;
-    
-    DEBUG(0, "parport_release(0x%p)\n", link);
-
-    if (info->ndev) {
-       struct parport *p = info->port;
-       parport_pc_unregister_port(p);
-       request_region(link->io.BasePort1, link->io.NumPorts1,
-                      info->node.dev_name);
-       if (link->io.NumPorts2)
-           request_region(link->io.BasePort2, link->io.NumPorts2,
-                          info->node.dev_name);
-    }
-    info->ndev = 0;
-    link->dev = NULL;
-    
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-    
-    link->state &= ~DEV_CONFIG;
-
-} /* parport_cs_release */
-
-static int parport_suspend(struct pcmcia_device *dev)
+void parport_cs_release(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
+       parport_info_t *info = link->priv;
 
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
+       DEBUG(0, "parport_release(0x%p)\n", link);
 
-       return 0;
-}
-
-static int parport_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
+       if (info->ndev) {
+               struct parport *p = info->port;
+               parport_pc_unregister_port(p);
+       }
+       info->ndev = 0;
 
-       link->state &= ~DEV_SUSPEND;
-       if (DEV_OK(link))
-               pcmcia_request_configuration(link->handle, &link->conf);
+       pcmcia_disable_device(link);
+} /* parport_cs_release */
 
-       return 0;
-}
 
 static struct pcmcia_device_id parport_ids[] = {
        PCMCIA_DEVICE_FUNC_ID(3),
@@ -328,11 +273,9 @@ static struct pcmcia_driver parport_cs_driver = {
        .drv            = {
                .name   = "parport_cs",
        },
-       .probe          = parport_attach,
+       .probe          = parport_probe,
        .remove         = parport_detach,
        .id_table       = parport_ids,
-       .suspend        = parport_suspend,
-       .resume         = parport_resume,
 };
 
 static int __init init_parport_cs(void)
index 1f4ad0e..cba6c9e 100644 (file)
@@ -263,6 +263,13 @@ config OMAP_CF
          Say Y here to support the CompactFlash controller on OMAP.
          Note that this doesn't support "True IDE" mode.
 
+config AT91_CF
+       tristate "AT91 CompactFlash Controller"
+       depends on PCMCIA && ARCH_AT91RM9200
+       help
+         Say Y here to support the CompactFlash controller on AT91 chips.
+         Or choose M to compile the driver as a module named "at91_cf".
+
 config PCCARD_NONSTATIC
        tristate
 
index bcecf51..4276965 100644 (file)
@@ -10,7 +10,7 @@ pcmcia_core-y                                 += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o
 pcmcia_core-$(CONFIG_CARDBUS)                  += cardbus.o
 obj-$(CONFIG_PCCARD)                           += pcmcia_core.o
 
-pcmcia-y                                       += ds.o pcmcia_compat.o pcmcia_resource.o
+pcmcia-y                                       += ds.o pcmcia_resource.o
 pcmcia-$(CONFIG_PCMCIA_IOCTL)                  += pcmcia_ioctl.o
 obj-$(CONFIG_PCMCIA)                           += pcmcia.o
 
@@ -36,6 +36,7 @@ obj-$(CONFIG_PCMCIA_AU1X00)                   += au1x00_ss.o
 obj-$(CONFIG_PCMCIA_VRC4171)                   += vrc4171_card.o
 obj-$(CONFIG_PCMCIA_VRC4173)                   += vrc4173_cardu.o
 obj-$(CONFIG_OMAP_CF)                          += omap_cf.o
+obj-$(CONFIG_AT91_CF)                          += at91_cf.o
 
 sa11xx_core-y                                  += soc_common.o sa11xx_base.o
 pxa2xx_core-y                                  += soc_common.o pxa2xx_base.o
diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c
new file mode 100644 (file)
index 0000000..67cc5f7
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * at91_cf.c -- AT91 CompactFlash controller driver
+ *
+ * Copyright (C) 2005 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <pcmcia/ss.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+
+#include <asm/arch/at91rm9200.h>
+#include <asm/arch/board.h>
+#include <asm/arch/gpio.h>
+
+
+#define        CF_SIZE         0x30000000      /* CS5+CS6: unavailable */
+
+/*
+ * A0..A10 work in each range; A23 indicates I/O space;  A25 is CFRNW;
+ * some other bit in {A24,A22..A11} is nREG to flag memory access
+ * (vs attributes).  So more than 2KB/region would just be waste.
+ */
+#define        CF_ATTR_PHYS    (AT91_CF_BASE)
+#define        CF_IO_PHYS      (AT91_CF_BASE  + (1 << 23))
+#define        CF_MEM_PHYS     (AT91_CF_BASE  + 0x017ff800)
+
+/*--------------------------------------------------------------------------*/
+
+static const char driver_name[] = "at91_cf";
+
+struct at91_cf_socket {
+       struct pcmcia_socket    socket;
+
+       unsigned                present:1;
+
+       struct platform_device  *pdev;
+       struct at91_cf_data     *board;
+};
+
+#define        SZ_2K                   (2 * SZ_1K)
+
+static inline int at91_cf_present(struct at91_cf_socket *cf)
+{
+       return !at91_get_gpio_value(cf->board->det_pin);
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int at91_cf_ss_init(struct pcmcia_socket *s)
+{
+       return 0;
+}
+
+static irqreturn_t at91_cf_irq(int irq, void *_cf, struct pt_regs *r)
+{
+       struct at91_cf_socket   *cf = (struct at91_cf_socket *) _cf;
+
+       if (irq == cf->board->det_pin) {
+               unsigned present = at91_cf_present(cf);
+
+               /* kick pccard as needed */
+               if (present != cf->present) {
+                       cf->present = present;
+                       pr_debug("%s: card %s\n", driver_name, present ? "present" : "gone");
+                       pcmcia_parse_events(&cf->socket, SS_DETECT);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int at91_cf_get_status(struct pcmcia_socket *s, u_int *sp)
+{
+       struct at91_cf_socket   *cf;
+
+       if (!sp)
+               return -EINVAL;
+
+       cf = container_of(s, struct at91_cf_socket, socket);
+
+       /* NOTE: we assume 3VCARD, not XVCARD...  */
+       if (at91_cf_present(cf)) {
+               int rdy = cf->board->irq_pin;   /* RDY/nIRQ */
+               int vcc = cf->board->vcc_pin;
+
+               *sp = SS_DETECT | SS_3VCARD;
+               if (!rdy || at91_get_gpio_value(rdy))
+                       *sp |= SS_READY;
+               if (!vcc || at91_get_gpio_value(vcc))
+                       *sp |= SS_POWERON;
+       } else
+               *sp = 0;
+
+       return 0;
+}
+
+static int at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
+{
+       struct at91_cf_socket   *cf;
+
+       cf = container_of(sock, struct at91_cf_socket, socket);
+
+       /* switch Vcc if needed and possible */
+       if (cf->board->vcc_pin) {
+               switch (s->Vcc) {
+                       case 0:
+                               at91_set_gpio_value(cf->board->vcc_pin, 0);
+                               break;
+                       case 33:
+                               at91_set_gpio_value(cf->board->vcc_pin, 1);
+                               break;
+                       default:
+                               return -EINVAL;
+               }
+       }
+
+       /* toggle reset if needed */
+       at91_set_gpio_value(cf->board->rst_pin, s->flags & SS_RESET);
+
+       pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n",
+               driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask);
+
+       return 0;
+}
+
+static int at91_cf_ss_suspend(struct pcmcia_socket *s)
+{
+       return at91_cf_set_socket(s, &dead_socket);
+}
+
+/* we already mapped the I/O region */
+static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
+{
+       struct at91_cf_socket   *cf;
+       u32                     csr;
+
+       cf = container_of(s, struct at91_cf_socket, socket);
+       io->flags &= (MAP_ACTIVE | MAP_16BIT | MAP_AUTOSZ);
+
+       /*
+        * Use 16 bit accesses unless/until we need 8-bit i/o space.
+        * Always set CSR4 ... PCMCIA won't always unmap things.
+        */
+       csr = at91_sys_read(AT91_SMC_CSR(4)) & ~AT91_SMC_DBW;
+
+       /*
+        * NOTE: this CF controller ignores IOIS16, so we can't really do
+        * MAP_AUTOSZ.  The 16bit mode allows single byte access on either
+        * D0-D7 (even addr) or D8-D15 (odd), so it's close enough for many
+        * purposes (and handles ide-cs).
+        *
+        * The 8bit mode is needed for odd byte access on D0-D7.  It seems
+        * some cards only like that way to get at the odd byte, despite
+        * CF 3.0 spec table 35 also giving the D8-D15 option.
+        */
+       if (!(io->flags & (MAP_16BIT|MAP_AUTOSZ))) {
+               csr |= AT91_SMC_DBW_8;
+               pr_debug("%s: 8bit i/o bus\n", driver_name);
+       } else {
+               csr |= AT91_SMC_DBW_16;
+               pr_debug("%s: 16bit i/o bus\n", driver_name);
+       }
+       at91_sys_write(AT91_SMC_CSR(4), csr);
+
+       io->start = cf->socket.io_offset;
+       io->stop = io->start + SZ_2K - 1;
+
+       return 0;
+}
+
+/* pcmcia layer maps/unmaps mem regions */
+static int at91_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
+{
+       struct at91_cf_socket   *cf;
+
+       if (map->card_start)
+               return -EINVAL;
+
+       cf = container_of(s, struct at91_cf_socket, socket);
+
+       map->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT;
+       if (map->flags & MAP_ATTRIB)
+               map->static_start = CF_ATTR_PHYS;
+       else
+               map->static_start = CF_MEM_PHYS;
+
+       return 0;
+}
+
+static struct pccard_operations at91_cf_ops = {
+       .init                   = at91_cf_ss_init,
+       .suspend                = at91_cf_ss_suspend,
+       .get_status             = at91_cf_get_status,
+       .set_socket             = at91_cf_set_socket,
+       .set_io_map             = at91_cf_set_io_map,
+       .set_mem_map            = at91_cf_set_mem_map,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int __init at91_cf_probe(struct device *dev)
+{
+       struct at91_cf_socket   *cf;
+       struct at91_cf_data     *board = dev->platform_data;
+       struct platform_device  *pdev = to_platform_device(dev);
+       unsigned int            csa;
+       int                     status;
+
+       if (!board || !board->det_pin || !board->rst_pin)
+               return -ENODEV;
+
+       cf = kcalloc(1, sizeof *cf, GFP_KERNEL);
+       if (!cf)
+               return -ENOMEM;
+
+       cf->board = board;
+       cf->pdev = pdev;
+       dev_set_drvdata(dev, cf);
+
+       /* CF takes over CS4, CS5, CS6 */
+       csa = at91_sys_read(AT91_EBI_CSA);
+       at91_sys_write(AT91_EBI_CSA, csa | AT91_EBI_CS4A_SMC_COMPACTFLASH);
+
+       /* force poweron defaults for these pins ... */
+       (void) at91_set_A_periph(AT91_PIN_PC9, 0);      /* A25/CFRNW */
+       (void) at91_set_A_periph(AT91_PIN_PC10, 0);     /* NCS4/CFCS */
+       (void) at91_set_A_periph(AT91_PIN_PC11, 0);     /* NCS5/CFCE1 */
+       (void) at91_set_A_periph(AT91_PIN_PC12, 0);     /* NCS6/CFCE2 */
+
+       /* nWAIT is _not_ a default setting */
+       (void) at91_set_A_periph(AT91_PIN_PC6, 1);      /*  nWAIT */
+
+       /*
+        * Static memory controller timing adjustments.
+        * REVISIT:  these timings are in terms of MCK cycles, so
+        * when MCK changes (cpufreq etc) so must these values...
+        */
+       at91_sys_write(AT91_SMC_CSR(4), AT91_SMC_ACSS_STD | AT91_SMC_DBW_16 | AT91_SMC_BAT | AT91_SMC_WSEN
+                               | AT91_SMC_NWS_(32)             /* wait states */
+                               | AT91_SMC_RWSETUP_(6)          /* setup time */
+                               | AT91_SMC_RWHOLD_(4)           /* hold time */
+       );
+
+       /* must be a GPIO; ergo must trigger on both edges */
+       status = request_irq(board->det_pin, at91_cf_irq,
+                       SA_SAMPLE_RANDOM, driver_name, cf);
+       if (status < 0)
+               goto fail0;
+
+       /*
+        * The card driver will request this irq later as needed.
+        * but it causes lots of "irqNN: nobody cared" messages
+        * unless we report that we handle everything (sigh).
+        * (Note:  DK board doesn't wire the IRQ pin...)
+        */
+       if (board->irq_pin) {
+               status = request_irq(board->irq_pin, at91_cf_irq,
+                               SA_SHIRQ, driver_name, cf);
+               if (status < 0)
+                       goto fail0a;
+               cf->socket.pci_irq = board->irq_pin;
+       }
+       else
+               cf->socket.pci_irq = NR_IRQS + 1;
+
+       /* pcmcia layer only remaps "real" memory not iospace */
+       cf->socket.io_offset = (unsigned long) ioremap(CF_IO_PHYS, SZ_2K);
+       if (!cf->socket.io_offset)
+               goto fail1;
+
+       /* reserve CS4, CS5, and CS6 regions; but use just CS4 */
+       if (!request_mem_region(AT91_CF_BASE, CF_SIZE, driver_name))
+               goto fail1;
+
+       pr_info("%s: irqs det #%d, io #%d\n", driver_name,
+               board->det_pin, board->irq_pin);
+
+       cf->socket.owner = THIS_MODULE;
+       cf->socket.dev.dev = dev;
+       cf->socket.ops = &at91_cf_ops;
+       cf->socket.resource_ops = &pccard_static_ops;
+       cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
+                               | SS_CAP_MEM_ALIGN;
+       cf->socket.map_size = SZ_2K;
+       cf->socket.io[0].NumPorts = SZ_2K;
+
+       status = pcmcia_register_socket(&cf->socket);
+       if (status < 0)
+               goto fail2;
+
+       return 0;
+
+fail2:
+       iounmap((void __iomem *) cf->socket.io_offset);
+       release_mem_region(AT91_CF_BASE, CF_SIZE);
+fail1:
+       if (board->irq_pin)
+               free_irq(board->irq_pin, cf);
+fail0a:
+       free_irq(board->det_pin, cf);
+fail0:
+       at91_sys_write(AT91_EBI_CSA, csa);
+       kfree(cf);
+       return status;
+}
+
+static int __exit at91_cf_remove(struct device *dev)
+{
+       struct at91_cf_socket *cf = dev_get_drvdata(dev);
+       unsigned int csa;
+
+       pcmcia_unregister_socket(&cf->socket);
+       free_irq(cf->board->irq_pin, cf);
+       free_irq(cf->board->det_pin, cf);
+       iounmap((void __iomem *) cf->socket.io_offset);
+       release_mem_region(AT91_CF_BASE, CF_SIZE);
+
+       csa = at91_sys_read(AT91_EBI_CSA);
+       at91_sys_write(AT91_EBI_CSA, csa & ~AT91_EBI_CS4A);
+
+       kfree(cf);
+       return 0;
+}
+
+static struct device_driver at91_cf_driver = {
+       .name           = (char *) driver_name,
+       .bus            = &platform_bus_type,
+       .probe          = at91_cf_probe,
+       .remove         = __exit_p(at91_cf_remove),
+       .suspend        = pcmcia_socket_dev_suspend,
+       .resume         = pcmcia_socket_dev_resume,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int __init at91_cf_init(void)
+{
+       return driver_register(&at91_cf_driver);
+}
+module_init(at91_cf_init);
+
+static void __exit at91_cf_exit(void)
+{
+       driver_unregister(&at91_cf_driver);
+}
+module_exit(at91_cf_exit);
+
+MODULE_DESCRIPTION("AT91 Compact Flash Driver");
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
index 120fa8d..912c03e 100644 (file)
@@ -12,7 +12,6 @@
  * (C) 1999            David A. Hinds
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/kernel.h>
index 613f2f1..3162998 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/config.h>
 #include <linux/string.h>
 #include <linux/major.h>
 #include <linux/errno.h>
@@ -111,9 +110,9 @@ int pcmcia_socket_dev_suspend(struct device *dev, pm_message_t state)
        list_for_each_entry(socket, &pcmcia_socket_list, socket_list) {
                if (socket->dev.dev != dev)
                        continue;
-               down(&socket->skt_sem);
+               mutex_lock(&socket->skt_mutex);
                socket_suspend(socket);
-               up(&socket->skt_sem);
+               mutex_unlock(&socket->skt_mutex);
        }
        up_read(&pcmcia_socket_list_rwsem);
 
@@ -129,9 +128,9 @@ int pcmcia_socket_dev_resume(struct device *dev)
        list_for_each_entry(socket, &pcmcia_socket_list, socket_list) {
                if (socket->dev.dev != dev)
                        continue;
-               down(&socket->skt_sem);
+               mutex_lock(&socket->skt_mutex);
                socket_resume(socket);
-               up(&socket->skt_sem);
+               mutex_unlock(&socket->skt_mutex);
        }
        up_read(&pcmcia_socket_list_rwsem);
 
@@ -237,7 +236,7 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
        init_completion(&socket->socket_released);
        init_completion(&socket->thread_done);
        init_waitqueue_head(&socket->thread_wait);
-       init_MUTEX(&socket->skt_sem);
+       mutex_init(&socket->skt_mutex);
        spin_lock_init(&socket->thread_lock);
 
        ret = kernel_thread(pccardd, socket, CLONE_KERNEL);
@@ -406,8 +405,6 @@ static void socket_shutdown(struct pcmcia_socket *s)
        cb_free(s);
 #endif
        s->functions = 0;
-       kfree(s->config);
-       s->config = NULL;
 
        s->ops->get_status(s, &status);
        if (status & SS_POWERON) {
@@ -664,7 +661,7 @@ static int pccardd(void *__skt)
                spin_unlock_irqrestore(&skt->thread_lock, flags);
 
                if (events) {
-                       down(&skt->skt_sem);
+                       mutex_lock(&skt->skt_mutex);
                        if (events & SS_DETECT)
                                socket_detect_change(skt);
                        if (events & SS_BATDEAD)
@@ -673,7 +670,7 @@ static int pccardd(void *__skt)
                                send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
                        if (events & SS_READY)
                                send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
-                       up(&skt->skt_sem);
+                       mutex_unlock(&skt->skt_mutex);
                        continue;
                }
 
@@ -717,8 +714,8 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
 {
         int ret = 0;
 
-       /* s->skt_sem also protects s->callback */
-       down(&s->skt_sem);
+       /* s->skt_mutex also protects s->callback */
+       mutex_lock(&s->skt_mutex);
 
        if (c) {
                /* registration */
@@ -734,7 +731,7 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
        } else
                s->callback = NULL;
  err:
-       up(&s->skt_sem);
+       mutex_unlock(&s->skt_mutex);
 
        return ret;
 }
@@ -752,7 +749,7 @@ int pccard_reset_card(struct pcmcia_socket *skt)
 
        cs_dbg(skt, 1, "resetting socket\n");
 
-       down(&skt->skt_sem);
+       mutex_lock(&skt->skt_mutex);
        do {
                if (!(skt->state & SOCKET_PRESENT)) {
                        ret = CS_NO_CARD;
@@ -781,7 +778,7 @@ int pccard_reset_card(struct pcmcia_socket *skt)
 
                ret = CS_SUCCESS;
        } while (0);
-       up(&skt->skt_sem);
+       mutex_unlock(&skt->skt_mutex);
 
        return ret;
 } /* reset_card */
@@ -797,7 +794,7 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt)
 
        cs_dbg(skt, 1, "suspending socket\n");
 
-       down(&skt->skt_sem);
+       mutex_lock(&skt->skt_mutex);
        do {
                if (!(skt->state & SOCKET_PRESENT)) {
                        ret = CS_NO_CARD;
@@ -814,7 +811,7 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt)
                }
                ret = socket_suspend(skt);
        } while (0);
-       up(&skt->skt_sem);
+       mutex_unlock(&skt->skt_mutex);
 
        return ret;
 } /* suspend_card */
@@ -827,7 +824,7 @@ int pcmcia_resume_card(struct pcmcia_socket *skt)
     
        cs_dbg(skt, 1, "waking up socket\n");
 
-       down(&skt->skt_sem);
+       mutex_lock(&skt->skt_mutex);
        do {
                if (!(skt->state & SOCKET_PRESENT)) {
                        ret = CS_NO_CARD;
@@ -841,7 +838,7 @@ int pcmcia_resume_card(struct pcmcia_socket *skt)
                if (!ret && skt->callback)
                        skt->callback->resume(skt);
        } while (0);
-       up(&skt->skt_sem);
+       mutex_unlock(&skt->skt_mutex);
 
        return ret;
 } /* resume_card */
@@ -855,7 +852,7 @@ int pcmcia_eject_card(struct pcmcia_socket *skt)
     
        cs_dbg(skt, 1, "user eject request\n");
 
-       down(&skt->skt_sem);
+       mutex_lock(&skt->skt_mutex);
        do {
                if (!(skt->state & SOCKET_PRESENT)) {
                        ret = -ENODEV;
@@ -871,7 +868,7 @@ int pcmcia_eject_card(struct pcmcia_socket *skt)
                socket_remove(skt);
                ret = 0;
        } while (0);
-       up(&skt->skt_sem);
+       mutex_unlock(&skt->skt_mutex);
 
        return ret;
 } /* eject_card */
@@ -884,7 +881,7 @@ int pcmcia_insert_card(struct pcmcia_socket *skt)
 
        cs_dbg(skt, 1, "user insert request\n");
 
-       down(&skt->skt_sem);
+       mutex_lock(&skt->skt_mutex);
        do {
                if (skt->state & SOCKET_PRESENT) {
                        ret = -EBUSY;
@@ -896,7 +893,7 @@ int pcmcia_insert_card(struct pcmcia_socket *skt)
                }
                ret = 0;
        } while (0);
-       up(&skt->skt_sem);
+       mutex_unlock(&skt->skt_mutex);
 
        return ret;
 } /* insert_card */
index 7b37eba..d6164cd 100644 (file)
@@ -15,7 +15,7 @@
 #ifndef _LINUX_CS_INTERNAL_H
 #define _LINUX_CS_INTERNAL_H
 
-#include <linux/config.h>
+#include <linux/kref.h>
 
 /* Flags in client state */
 #define CLIENT_CONFIG_LOCKED   0x0001
@@ -23,7 +23,7 @@
 #define CLIENT_IO_REQ          0x0004
 #define CLIENT_UNBOUND         0x0008
 #define CLIENT_STALE           0x0010
-#define CLIENT_WIN_REQ(i)      (0x20<<(i))
+#define CLIENT_WIN_REQ(i)      (0x1<<(i))
 #define CLIENT_CARDBUS         0x8000
 
 #define REGION_MAGIC   0xE3C9
@@ -31,7 +31,7 @@ typedef struct region_t {
     u_short            region_magic;
     u_short            state;
     dev_info_t         dev_info;
-    client_handle_t    mtd;
+    struct pcmcia_device       *mtd;
     u_int              MediaID;
     region_info_t      info;
 } region_t;
@@ -40,12 +40,12 @@ typedef struct region_t {
 
 /* Each card function gets one of these guys */
 typedef struct config_t {
+       struct kref     ref;
     u_int              state;
     u_int              Attributes;
     u_int              IntType;
     u_int              ConfigBase;
     u_char             Status, Pin, Copy, Option, ExtStatus;
-    u_int              Present;
     u_int              CardValues;
     io_req_t           io;
     struct {
@@ -95,12 +95,6 @@ static inline void cs_socket_put(struct pcmcia_socket *skt)
        }
 }
 
-#define CHECK_SOCKET(s) \
-    (((s) >= sockets) || (socket_table[s]->ops == NULL))
-
-#define SOCKET(h) (h->socket)
-#define CONFIG(h) (&SOCKET(h)->config[(h)->func])
-
 /* In cardbus.c */
 int cb_alloc(struct pcmcia_socket *s);
 void cb_free(struct pcmcia_socket *s);
@@ -133,10 +127,9 @@ extern struct class_interface pccard_sysfs_interface;
 extern struct rw_semaphore pcmcia_socket_list_rwsem;
 extern struct list_head pcmcia_socket_list;
 int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req);
-int pccard_get_configuration_info(struct pcmcia_socket *s, unsigned int function, config_info_t *config);
+int pccard_get_configuration_info(struct pcmcia_socket *s, struct pcmcia_device *p_dev, config_info_t *config);
 int pccard_reset_card(struct pcmcia_socket *skt);
-int pccard_get_status(struct pcmcia_socket *s, unsigned int function, cs_status_t *status);
-int pccard_access_configuration_register(struct pcmcia_socket *s, unsigned int function, conf_reg_t *reg);
+int pccard_get_status(struct pcmcia_socket *s, struct pcmcia_device *p_dev, cs_status_t *status);
 
 
 struct pcmcia_callback{
index bb96ce1..ae10d1e 100644 (file)
  * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
  *
  * (C) 1999            David A. Hinds
- * (C) 2003 - 2005     Dominik Brodowski
+ * (C) 2003 - 2006     Dominik Brodowski
  */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -23,6 +22,7 @@
 #include <linux/workqueue.h>
 #include <linux/crc32.h>
 #include <linux/firmware.h>
+#include <linux/kref.h>
 
 #define IN_CARD_SERVICES
 #include <pcmcia/cs_types.h>
@@ -343,12 +343,19 @@ void pcmcia_put_dev(struct pcmcia_device *p_dev)
                put_device(&p_dev->dev);
 }
 
+static void pcmcia_release_function(struct kref *ref)
+{
+       struct config_t *c = container_of(ref, struct config_t, ref);
+       kfree(c);
+}
+
 static void pcmcia_release_dev(struct device *dev)
 {
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
        ds_dbg(1, "releasing dev %p\n", p_dev);
        pcmcia_put_socket(p_dev->socket);
        kfree(p_dev->devname);
+       kref_put(&p_dev->function_config->ref, pcmcia_release_function);
        kfree(p_dev);
 }
 
@@ -377,29 +384,12 @@ static int pcmcia_device_probe(struct device * dev)
        p_drv = to_pcmcia_drv(dev->driver);
        s = p_dev->socket;
 
-       if ((!p_drv->probe) || (!try_module_get(p_drv->owner))) {
+       if ((!p_drv->probe) || (!p_dev->function_config) ||
+           (!try_module_get(p_drv->owner))) {
                ret = -EINVAL;
                goto put_dev;
        }
 
-       p_dev->state &= ~CLIENT_UNBOUND;
-
-       /* set up the device configuration, if it hasn't been done before */
-       if (!s->functions) {
-               cistpl_longlink_mfc_t mfc;
-               if (pccard_read_tuple(s, p_dev->func, CISTPL_LONGLINK_MFC,
-                                     &mfc) == CS_SUCCESS)
-                       s->functions = mfc.nfn;
-               else
-                       s->functions = 1;
-               s->config = kzalloc(sizeof(config_t) * s->functions,
-                                   GFP_KERNEL);
-               if (!s->config) {
-                       ret = -ENOMEM;
-                       goto put_module;
-               }
-       }
-
        ret = p_drv->probe(p_dev);
        if (ret)
                goto put_module;
@@ -425,15 +415,61 @@ static int pcmcia_device_probe(struct device * dev)
 }
 
 
+/*
+ * Removes a PCMCIA card from the device tree and socket list.
+ */
+static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *leftover)
+{
+       struct pcmcia_device    *p_dev;
+       struct pcmcia_device    *tmp;
+       unsigned long           flags;
+
+       ds_dbg(2, "unbind_request(%d)\n", s->sock);
+
+
+       if (!leftover)
+               s->device_count = 0;
+       else
+               s->device_count = 1;
+
+       /* unregister all pcmcia_devices registered with this socket, except leftover */
+       list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) {
+               if (p_dev == leftover)
+                       continue;
+
+               spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+               list_del(&p_dev->socket_device_list);
+               p_dev->_removed=1;
+               spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+               device_unregister(&p_dev->dev);
+       }
+
+       return;
+}
+
+
 static int pcmcia_device_remove(struct device * dev)
 {
        struct pcmcia_device *p_dev;
        struct pcmcia_driver *p_drv;
+       struct pcmcia_device_id *did;
        int i;
 
-       /* detach the "instance" */
        p_dev = to_pcmcia_dev(dev);
        p_drv = to_pcmcia_drv(dev->driver);
+
+       /* If we're removing the primary module driving a
+        * pseudo multi-function card, we need to unbind
+        * all devices
+        */
+       did = (struct pcmcia_device_id *) p_dev->dev.driver_data;
+       if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
+           (p_dev->socket->device_count != 0) &&
+           (p_dev->device_no == 0))
+               pcmcia_card_remove(p_dev->socket, p_dev);
+
+       /* detach the "instance" */
        if (!p_drv)
                return 0;
 
@@ -441,17 +477,16 @@ static int pcmcia_device_remove(struct device * dev)
                p_drv->remove(p_dev);
 
        /* check for proper unloading */
-       if (p_dev->state & (CLIENT_IRQ_REQ|CLIENT_IO_REQ|CLIENT_CONFIG_LOCKED))
+       if (p_dev->_irq || p_dev->_io || p_dev->_locked)
                printk(KERN_INFO "pcmcia: driver %s did not release config properly\n",
                       p_drv->drv.name);
 
        for (i = 0; i < MAX_WIN; i++)
-               if (p_dev->state & CLIENT_WIN_REQ(i))
+               if (p_dev->_win & CLIENT_WIN_REQ(i))
                        printk(KERN_INFO "pcmcia: driver %s did not release windows properly\n",
                               p_drv->drv.name);
 
        /* references from pcmcia_probe_device */
-       p_dev->state = CLIENT_UNBOUND;
        pcmcia_put_dev(p_dev);
        module_put(p_drv->owner);
 
@@ -459,37 +494,6 @@ static int pcmcia_device_remove(struct device * dev)
 }
 
 
-/*
- * Removes a PCMCIA card from the device tree and socket list.
- */
-static void pcmcia_card_remove(struct pcmcia_socket *s)
-{
-       struct pcmcia_device    *p_dev;
-       unsigned long           flags;
-
-       ds_dbg(2, "unbind_request(%d)\n", s->sock);
-
-       s->device_count = 0;
-
-       for (;;) {
-               /* unregister all pcmcia_devices registered with this socket*/
-               spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
-               if (list_empty(&s->devices_list)) {
-                       spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
-                       return;
-               }
-               p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list);
-               list_del(&p_dev->socket_device_list);
-               p_dev->state |= CLIENT_STALE;
-               spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
-
-               device_unregister(&p_dev->dev);
-       }
-
-       return;
-} /* unbind_request */
-
-
 /*
  * pcmcia_device_query -- determine information about a pcmcia device
  */
@@ -546,7 +550,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
                        tmp = vers1->str + vers1->ofs[i];
 
                        length = strlen(tmp) + 1;
-                       if ((length < 3) || (length > 255))
+                       if ((length < 2) || (length > 255))
                                continue;
 
                        p_dev->prod_id[i] = kmalloc(sizeof(char) * length,
@@ -571,11 +575,11 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
  * won't work, this doesn't matter much at the moment: the driver core doesn't
  * support it either.
  */
-static DECLARE_MUTEX(device_add_lock);
+static DEFINE_MUTEX(device_add_lock);
 
 struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
 {
-       struct pcmcia_device *p_dev;
+       struct pcmcia_device *p_dev, *tmp_dev;
        unsigned long flags;
        int bus_id_len;
 
@@ -583,7 +587,7 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
        if (!s)
                return NULL;
 
-       down(&device_add_lock);
+       mutex_lock(&device_add_lock);
 
        /* max of 2 devices per card */
        if (s->device_count == 2)
@@ -596,6 +600,8 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
        p_dev->socket = s;
        p_dev->device_no = (s->device_count++);
        p_dev->func   = function;
+       if (s->functions <= function)
+               s->functions = function + 1;
 
        p_dev->dev.bus = &pcmcia_bus_type;
        p_dev->dev.parent = s->dev.dev;
@@ -608,36 +614,55 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
        sprintf (p_dev->devname, "pcmcia%s", p_dev->dev.bus_id);
 
        /* compat */
-       p_dev->state = CLIENT_UNBOUND;
+       spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+
+       /*
+        * p_dev->function_config must be the same for all card functions.
+        * Note that this is serialized by the device_add_lock, so that
+        * only one such struct will be created.
+        */
+        list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list)
+                if (p_dev->func == tmp_dev->func) {
+                       p_dev->function_config = tmp_dev->function_config;
+                       kref_get(&p_dev->function_config->ref);
+               }
 
        /* Add to the list in pcmcia_bus_socket */
-       spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
        list_add_tail(&p_dev->socket_device_list, &s->devices_list);
+
        spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
 
+       if (!p_dev->function_config) {
+               p_dev->function_config = kzalloc(sizeof(struct config_t),
+                                                GFP_KERNEL);
+               if (!p_dev->function_config)
+                       goto err_unreg;
+               kref_init(&p_dev->function_config->ref);
+       }
+
        printk(KERN_NOTICE "pcmcia: registering new device %s\n",
               p_dev->devname);
 
        pcmcia_device_query(p_dev);
 
-       if (device_register(&p_dev->dev)) {
-               spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
-               list_del(&p_dev->socket_device_list);
-               spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
-
-               goto err_free;
-       }
+       if (device_register(&p_dev->dev))
+               goto err_unreg;
 
-       up(&device_add_lock);
+       mutex_unlock(&device_add_lock);
 
        return p_dev;
 
+ err_unreg:
+       spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+       list_del(&p_dev->socket_device_list);
+       spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
  err_free:
        kfree(p_dev->devname);
        kfree(p_dev);
        s->device_count--;
  err_put:
-       up(&device_add_lock);
+       mutex_unlock(&device_add_lock);
        pcmcia_put_socket(s);
 
        return NULL;
@@ -696,7 +721,7 @@ static void pcmcia_bus_rescan(struct pcmcia_socket *skt)
        int no_devices=0;
        unsigned long flags;
 
-       /* must be called with skt_sem held */
+       /* must be called with skt_mutex held */
        spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
        if (list_empty(&skt->devices_list))
                no_devices=1;
@@ -819,9 +844,11 @@ static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) {
        struct pcmcia_driver * p_drv = to_pcmcia_drv(drv);
        struct pcmcia_device_id *did = p_drv->id_table;
 
+#ifdef CONFIG_PCMCIA_IOCTL
        /* matching by cardmgr */
        if (p_dev->cardmgr == p_drv)
                return 1;
+#endif
 
        while (did && did->match_flags) {
                if (pcmcia_devmatch(p_dev, did))
@@ -927,7 +954,7 @@ static ssize_t pcmcia_show_pm_state(struct device *dev, struct device_attribute
 {
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
 
-       if (p_dev->dev.power.power_state.event != PM_EVENT_ON)
+       if (p_dev->suspended)
                return sprintf(buf, "off\n");
        else
                return sprintf(buf, "on\n");
@@ -942,11 +969,9 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute
         if (!count)
                 return -EINVAL;
 
-       if ((p_dev->dev.power.power_state.event == PM_EVENT_ON) &&
-           (!strncmp(buf, "off", 3)))
+       if ((!p_dev->suspended) && !strncmp(buf, "off", 3))
                ret = dpm_runtime_suspend(dev, PMSG_SUSPEND);
-       else if ((p_dev->dev.power.power_state.event != PM_EVENT_ON) &&
-                (!strncmp(buf, "on", 2)))
+       else if (p_dev->suspended && !strncmp(buf, "on", 2))
                dpm_runtime_resume(dev);
 
        return ret ? ret : count;
@@ -982,9 +1007,9 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev,
        if (!count)
                return -EINVAL;
 
-       down(&p_dev->socket->skt_sem);
+       mutex_lock(&p_dev->socket->skt_mutex);
        p_dev->allow_func_id_match = 1;
-       up(&p_dev->socket->skt_sem);
+       mutex_unlock(&p_dev->socket->skt_mutex);
 
        bus_rescan_devices(&pcmcia_bus_type);
 
@@ -1012,14 +1037,27 @@ static int pcmcia_dev_suspend(struct device * dev, pm_message_t state)
 {
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
        struct pcmcia_driver *p_drv = NULL;
+       int ret = 0;
 
        if (dev->driver)
                p_drv = to_pcmcia_drv(dev->driver);
 
-       if (p_drv && p_drv->suspend)
-               return p_drv->suspend(p_dev);
+       if (!p_drv)
+               goto out;
 
-       return 0;
+       if (p_drv->suspend) {
+               ret = p_drv->suspend(p_dev);
+               if (ret)
+                       goto out;
+       }
+
+       if (p_dev->device_no == p_dev->func)
+               pcmcia_release_configuration(p_dev);
+
+ out:
+       if (!ret)
+               p_dev->suspended = 1;
+       return ret;
 }
 
 
@@ -1027,14 +1065,27 @@ static int pcmcia_dev_resume(struct device * dev)
 {
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
         struct pcmcia_driver *p_drv = NULL;
+       int ret = 0;
 
        if (dev->driver)
                p_drv = to_pcmcia_drv(dev->driver);
 
-       if (p_drv && p_drv->resume)
-               return p_drv->resume(p_dev);
+       if (!p_drv)
+               goto out;
 
-       return 0;
+       if (p_dev->device_no == p_dev->func) {
+               ret = pcmcia_request_configuration(p_dev, &p_dev->conf);
+               if (ret)
+                       goto out;
+       }
+
+       if (p_drv->resume)
+               ret = p_drv->resume(p_dev);
+
+ out:
+       if (!ret)
+               p_dev->suspended = 0;
+       return ret;
 }
 
 
@@ -1100,7 +1151,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
        switch (event) {
        case CS_EVENT_CARD_REMOVAL:
                s->pcmcia_state.present = 0;
-               pcmcia_card_remove(skt);
+               pcmcia_card_remove(skt, NULL);
                handle_event(skt, event);
                break;
 
@@ -1128,6 +1179,32 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
 } /* ds_event */
 
 
+struct pcmcia_device * pcmcia_dev_present(struct pcmcia_device *_p_dev)
+{
+       struct pcmcia_device *p_dev;
+       struct pcmcia_device *ret = NULL;
+
+       p_dev = pcmcia_get_dev(_p_dev);
+       if (!p_dev)
+               return NULL;
+
+       if (!p_dev->socket->pcmcia_state.present)
+               goto out;
+
+       if (p_dev->_removed)
+               goto out;
+
+       if (p_dev->suspended)
+               goto out;
+
+       ret = p_dev;
+ out:
+       pcmcia_put_dev(p_dev);
+       return ret;
+}
+EXPORT_SYMBOL(pcmcia_dev_present);
+
+
 static struct pcmcia_callback pcmcia_bus_callback = {
        .owner = THIS_MODULE,
        .event = ds_event,
index d359bd2..3a2b25e 100644 (file)
@@ -8,6 +8,8 @@ extern void pcmcia_put_dev(struct pcmcia_device *p_dev);
 
 struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function);
 
+extern int pcmcia_release_configuration(struct pcmcia_device *p_dev);
+
 #ifdef CONFIG_PCMCIA_IOCTL
 extern void __init pcmcia_setup_ioctl(void);
 extern void __exit pcmcia_cleanup_ioctl(void);
@@ -15,7 +17,7 @@ extern void handle_event(struct pcmcia_socket *s, event_t event);
 extern int handle_request(struct pcmcia_socket *s, event_t event);
 #else
 static inline void __init pcmcia_setup_ioctl(void) { return; }
-static inline void __init pcmcia_cleanup_ioctl(void) { return; }
+static inline void __exit pcmcia_cleanup_ioctl(void) { return; }
 static inline void handle_event(struct pcmcia_socket *s, event_t event) { return; }
 static inline int handle_request(struct pcmcia_socket *s, event_t event) { return CS_SUCCESS; }
 #endif
index 7979c85..d5f03a3 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/init.h>
index 35a92d1..bd0308e 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
 #include <linux/string.h>
diff --git a/drivers/pcmcia/pcmcia_compat.c b/drivers/pcmcia/pcmcia_compat.c
deleted file mode 100644 (file)
index ebb161c..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * PCMCIA 16-bit compatibility functions
- *
- * The initial developer of the original code is David A. Hinds
- * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
- * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
- *
- * Copyright (C) 2004 Dominik Brodowski
- *
- * 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/config.h>
-#include <linux/module.h>
-#include <linux/init.h>
-
-#define IN_CARD_SERVICES
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/bulkmem.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/ss.h>
-
-#include "cs_internal.h"
-
-int pcmcia_get_first_tuple(struct pcmcia_device *p_dev, tuple_t *tuple)
-{
-       return pccard_get_first_tuple(p_dev->socket, p_dev->func, tuple);
-}
-EXPORT_SYMBOL(pcmcia_get_first_tuple);
-
-int pcmcia_get_next_tuple(struct pcmcia_device *p_dev, tuple_t *tuple)
-{
-       return pccard_get_next_tuple(p_dev->socket, p_dev->func, tuple);
-}
-EXPORT_SYMBOL(pcmcia_get_next_tuple);
-
-int pcmcia_get_tuple_data(struct pcmcia_device *p_dev, tuple_t *tuple)
-{
-       return pccard_get_tuple_data(p_dev->socket, tuple);
-}
-EXPORT_SYMBOL(pcmcia_get_tuple_data);
-
-int pcmcia_parse_tuple(struct pcmcia_device *p_dev, tuple_t *tuple, cisparse_t *parse)
-{
-       return pccard_parse_tuple(tuple, parse);
-}
-EXPORT_SYMBOL(pcmcia_parse_tuple);
-
-int pcmcia_validate_cis(struct pcmcia_device *p_dev, cisinfo_t *info)
-{
-       return pccard_validate_cis(p_dev->socket, p_dev->func, info);
-}
-EXPORT_SYMBOL(pcmcia_validate_cis);
-
-
-int pcmcia_reset_card(struct pcmcia_device *p_dev, client_req_t *req)
-{
-       return pccard_reset_card(p_dev->socket);
-}
-EXPORT_SYMBOL(pcmcia_reset_card);
index 80969f7..c53db7c 100644 (file)
@@ -18,7 +18,6 @@
  */
 
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -70,10 +69,26 @@ extern int ds_pc_debug;
 #define ds_dbg(lvl, fmt, arg...) do { } while (0)
 #endif
 
+static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s,
+                                               unsigned int function)
+{
+       struct pcmcia_device *p_dev = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+       list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
+               if (p_dev->func == function) {
+                       spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+                       return pcmcia_get_dev(p_dev);
+               }
+       }
+       spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+       return NULL;
+}
 
 /* backwards-compatible accessing of driver --- by name! */
 
-static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info)
+static struct pcmcia_driver *get_pcmcia_driver(dev_info_t *dev_info)
 {
        struct device_driver *drv;
        struct pcmcia_driver *p_drv;
@@ -214,7 +229,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
                                         * by userspace before, we need to
                                         * return the "instance". */
                                        spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
-                                       bind_info->instance = p_dev->instance;
+                                       bind_info->instance = p_dev;
                                        ret = -EBUSY;
                                        goto err_put_module;
                                } else {
@@ -253,9 +268,9 @@ rescan:
        /*
         * Prevent this racing with a card insertion.
         */
-       down(&s->skt_sem);
+       mutex_lock(&s->skt_mutex);
        bus_rescan_devices(&pcmcia_bus_type);
-       up(&s->skt_sem);
+       mutex_unlock(&s->skt_mutex);
 
        /* check whether the driver indeed matched. I don't care if this
         * is racy or not, because it can only happen on cardmgr access
@@ -289,6 +304,7 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int
 {
        dev_node_t *node;
        struct pcmcia_device *p_dev;
+       struct pcmcia_driver *p_drv;
        unsigned long flags;
        int ret = 0;
 
@@ -343,16 +359,16 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int
  found:
        spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
 
-       if ((!p_dev->instance) ||
-           (p_dev->instance->state & DEV_CONFIG_PENDING)) {
+       p_drv = to_pcmcia_drv(p_dev->dev.driver);
+       if (p_drv && !p_dev->_locked) {
                ret = -EAGAIN;
                goto err_put;
        }
 
        if (first)
-               node = p_dev->instance->dev;
+               node = p_dev->dev_node;
        else
-               for (node = p_dev->instance->dev; node; node = node->next)
+               for (node = p_dev->dev_node; node; node = node->next)
                        if (node == bind_info->next)
                                break;
        if (!node) {
@@ -583,14 +599,16 @@ static int ds_ioctl(struct inode * inode, struct file * file,
        if (buf->config.Function &&
           (buf->config.Function >= s->functions))
            ret = CS_BAD_ARGS;
-       else
-           ret = pccard_get_configuration_info(s,
-                       buf->config.Function, &buf->config);
+       else {
+           struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->config.Function);
+           ret = pccard_get_configuration_info(s, p_dev, &buf->config);
+           pcmcia_put_dev(p_dev);
+       }
        break;
     case DS_GET_FIRST_TUPLE:
-       down(&s->skt_sem);
+       mutex_lock(&s->skt_mutex);
        pcmcia_validate_mem(s);
-       up(&s->skt_sem);
+       mutex_unlock(&s->skt_mutex);
        ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple);
        break;
     case DS_GET_NEXT_TUPLE:
@@ -609,16 +627,19 @@ static int ds_ioctl(struct inode * inode, struct file * file,
        ret = pccard_reset_card(s);
        break;
     case DS_GET_STATUS:
-       if (buf->status.Function &&
-          (buf->status.Function >= s->functions))
-           ret = CS_BAD_ARGS;
-       else
-       ret = pccard_get_status(s, buf->status.Function, &buf->status);
-       break;
+           if (buf->status.Function &&
+               (buf->status.Function >= s->functions))
+                   ret = CS_BAD_ARGS;
+           else {
+                   struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->status.Function);
+                   ret = pccard_get_status(s, p_dev, &buf->status);
+                   pcmcia_put_dev(p_dev);
+           }
+           break;
     case DS_VALIDATE_CIS:
-       down(&s->skt_sem);
+       mutex_lock(&s->skt_mutex);
        pcmcia_validate_mem(s);
-       up(&s->skt_sem);
+       mutex_unlock(&s->skt_mutex);
        ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo);
        break;
     case DS_SUSPEND_CARD:
@@ -638,12 +659,16 @@ static int ds_ioctl(struct inode * inode, struct file * file,
            err = -EPERM;
            goto free_out;
        }
-       if (buf->conf_reg.Function &&
-          (buf->conf_reg.Function >= s->functions))
-           ret = CS_BAD_ARGS;
-       else
-           ret = pccard_access_configuration_register(s,
-                       buf->conf_reg.Function, &buf->conf_reg);
+
+       ret = CS_BAD_ARGS;
+
+       if (!(buf->conf_reg.Function &&
+            (buf->conf_reg.Function >= s->functions))) {
+               struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->conf_reg.Function);
+               if (p_dev)
+                       ret = pcmcia_access_configuration_register(p_dev, &buf->conf_reg);
+               pcmcia_put_dev(p_dev);
+       }
        break;
     case DS_GET_FIRST_REGION:
     case DS_GET_NEXT_REGION:
index 89022ad..45063b4 100644 (file)
@@ -14,7 +14,6 @@
  *
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
@@ -89,7 +88,7 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base,
        }
        if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) {
                *base = s->io_offset | (*base & 0x0fff);
-               s->io[0].Attributes = attr;
+               s->io[0].res->flags = (s->io[0].res->flags & ~IORESOURCE_BITS) | (attr & IORESOURCE_BITS);
                return 0;
        }
        /* Check for an already-allocated window that must conflict with
@@ -97,38 +96,36 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base,
         * potential conflicts, just the most obvious ones.
         */
        for (i = 0; i < MAX_IO_WIN; i++)
-               if ((s->io[i].NumPorts != 0) &&
-                   ((s->io[i].BasePort & (align-1)) == *base))
+               if ((s->io[i].res) &&
+                   ((s->io[i].res->start & (align-1)) == *base))
                        return 1;
        for (i = 0; i < MAX_IO_WIN; i++) {
-               if (s->io[i].NumPorts == 0) {
+               if (!s->io[i].res) {
                        s->io[i].res = pcmcia_find_io_region(*base, num, align, s);
                        if (s->io[i].res) {
-                               s->io[i].Attributes = attr;
-                               s->io[i].BasePort = *base = s->io[i].res->start;
-                               s->io[i].NumPorts = s->io[i].InUse = num;
+                               *base = s->io[i].res->start;
+                               s->io[i].res->flags = (s->io[i].res->flags & ~IORESOURCE_BITS) | (attr & IORESOURCE_BITS);
+                               s->io[i].InUse = num;
                                break;
                        } else
                                return 1;
-               } else if (s->io[i].Attributes != attr)
+               } else if ((s->io[i].res->flags & IORESOURCE_BITS) != (attr & IORESOURCE_BITS))
                        continue;
                /* Try to extend top of window */
-               try = s->io[i].BasePort + s->io[i].NumPorts;
+               try = s->io[i].res->end + 1;
                if ((*base == 0) || (*base == try))
                        if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start,
                                                    s->io[i].res->end + num, s) == 0) {
                                *base = try;
-                               s->io[i].NumPorts += num;
                                s->io[i].InUse += num;
                                break;
                        }
                /* Try to extend bottom of window */
-               try = s->io[i].BasePort - num;
+               try = s->io[i].res->start - num;
                if ((*base == 0) || (*base == try))
                        if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start - num,
                                                    s->io[i].res->end, s) == 0) {
-                               s->io[i].BasePort = *base = try;
-                               s->io[i].NumPorts += num;
+                               *base = try;
                                s->io[i].InUse += num;
                                break;
                        }
@@ -143,12 +140,13 @@ static void release_io_space(struct pcmcia_socket *s, ioaddr_t base,
        int i;
 
        for (i = 0; i < MAX_IO_WIN; i++) {
-               if ((s->io[i].BasePort <= base) &&
-                   (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) {
+               if (!s->io[i].res)
+                       continue;
+               if ((s->io[i].res->start <= base) &&
+                   (s->io[i].res->end >= base+num-1)) {
                        s->io[i].InUse -= num;
                        /* Free the window if no one else is using it */
                        if (s->io[i].InUse == 0) {
-                               s->io[i].NumPorts = 0;
                                release_resource(s->io[i].res);
                                kfree(s->io[i].res);
                                s->io[i].res = NULL;
@@ -165,21 +163,19 @@ static void release_io_space(struct pcmcia_socket *s, ioaddr_t base,
  * this and the tuple reading services.
  */
 
-int pccard_access_configuration_register(struct pcmcia_socket *s,
-                                        unsigned int function,
+int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
                                         conf_reg_t *reg)
 {
+       struct pcmcia_socket *s;
        config_t *c;
        int addr;
        u_char val;
 
-       if (!s || !s->config)
+       if (!p_dev || !p_dev->function_config)
                return CS_NO_CARD;
 
-       c = &s->config[function];
-
-       if (c == NULL)
-               return CS_NO_CARD;
+       s = p_dev->socket;
+       c = p_dev->function_config;
 
        if (!(c->state & CONFIG_LOCKED))
                return CS_CONFIGURATION_LOCKED;
@@ -200,20 +196,12 @@ int pccard_access_configuration_register(struct pcmcia_socket *s,
                break;
        }
        return CS_SUCCESS;
-} /* pccard_access_configuration_register */
-
-int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
-                                        conf_reg_t *reg)
-{
-       return pccard_access_configuration_register(p_dev->socket,
-                                                   p_dev->func, reg);
-}
+} /* pcmcia_access_configuration_register */
 EXPORT_SYMBOL(pcmcia_access_configuration_register);
 
 
-
 int pccard_get_configuration_info(struct pcmcia_socket *s,
-                                 unsigned int function,
+                                 struct pcmcia_device *p_dev,
                                  config_info_t *config)
 {
        config_t *c;
@@ -221,7 +209,7 @@ int pccard_get_configuration_info(struct pcmcia_socket *s,
        if (!(s->state & SOCKET_PRESENT))
                return CS_NO_CARD;
 
-       config->Function = function;
+       config->Function = p_dev->func;
 
 #ifdef CONFIG_CARDBUS
        if (s->state & SOCKET_CARDBUS) {
@@ -235,14 +223,14 @@ int pccard_get_configuration_info(struct pcmcia_socket *s,
                        config->AssignedIRQ = s->irq.AssignedIRQ;
                        if (config->AssignedIRQ)
                                config->Attributes |= CONF_ENABLE_IRQ;
-                       config->BasePort1 = s->io[0].BasePort;
-                       config->NumPorts1 = s->io[0].NumPorts;
+                       config->BasePort1 = s->io[0].res->start;
+                       config->NumPorts1 = s->io[0].res->end - config->BasePort1 + 1;
                }
                return CS_SUCCESS;
        }
 #endif
 
-       c = (s->config != NULL) ? &s->config[function] : NULL;
+       c = (p_dev) ? p_dev->function_config : NULL;
 
        if ((c == NULL) || !(c->state & CONFIG_LOCKED)) {
                config->Attributes = 0;
@@ -271,7 +259,7 @@ int pccard_get_configuration_info(struct pcmcia_socket *s,
 int pcmcia_get_configuration_info(struct pcmcia_device *p_dev,
                                  config_info_t *config)
 {
-       return pccard_get_configuration_info(p_dev->socket, p_dev->func,
+       return pccard_get_configuration_info(p_dev->socket, p_dev,
                                             config);
 }
 EXPORT_SYMBOL(pcmcia_get_configuration_info);
@@ -317,7 +305,7 @@ EXPORT_SYMBOL(pcmcia_get_window);
  * SocketState yet: I haven't seen any point for it.
  */
 
-int pccard_get_status(struct pcmcia_socket *s, unsigned int function,
+int pccard_get_status(struct pcmcia_socket *s, struct pcmcia_device *p_dev,
                      cs_status_t *status)
 {
        config_t *c;
@@ -334,11 +322,12 @@ int pccard_get_status(struct pcmcia_socket *s, unsigned int function,
        if (!(s->state & SOCKET_PRESENT))
                return CS_NO_CARD;
 
-       c = (s->config != NULL) ? &s->config[function] : NULL;
+       c = (p_dev) ? p_dev->function_config : NULL;
+
        if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
            (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
                u_char reg;
-               if (c->Present & PRESENT_PIN_REPLACE) {
+               if (c->CardValues & PRESENT_PIN_REPLACE) {
                        pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
                        status->CardState |=
                                (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0;
@@ -352,7 +341,7 @@ int pccard_get_status(struct pcmcia_socket *s, unsigned int function,
                        /* No PRR?  Then assume we're always ready */
                        status->CardState |= CS_EVENT_READY_CHANGE;
                }
-               if (c->Present & PRESENT_EXT_STATUS) {
+               if (c->CardValues & PRESENT_EXT_STATUS) {
                        pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
                        status->CardState |=
                                (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
@@ -370,11 +359,9 @@ int pccard_get_status(struct pcmcia_socket *s, unsigned int function,
        return CS_SUCCESS;
 } /* pccard_get_status */
 
-int pcmcia_get_status(client_handle_t handle, cs_status_t *status)
+int pcmcia_get_status(struct pcmcia_device *p_dev, cs_status_t *status)
 {
-       struct pcmcia_socket *s;
-       s = SOCKET(handle);
-       return pccard_get_status(s, handle->func, status);
+       return pccard_get_status(p_dev->socket, p_dev, status);
 }
 EXPORT_SYMBOL(pcmcia_get_status);
 
@@ -422,7 +409,8 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
        config_t *c;
 
        s = p_dev->socket;
-       c = CONFIG(p_dev);
+       c = p_dev->function_config;
+
        if (!(s->state & SOCKET_PRESENT))
                return CS_NO_CARD;
        if (!(c->state & CONFIG_LOCKED))
@@ -454,6 +442,28 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
                   (mod->Attributes & CONF_VPP2_CHANGE_VALID))
                return CS_BAD_VPP;
 
+       if (mod->Attributes & CONF_IO_CHANGE_WIDTH) {
+               pccard_io_map io_off = { 0, 0, 0, 0, 1 };
+               pccard_io_map io_on;
+               int i;
+
+               io_on.speed = io_speed;
+               for (i = 0; i < MAX_IO_WIN; i++) {
+                       if (!s->io[i].res)
+                               continue;
+                       io_off.map = i;
+                       io_on.map = i;
+
+                       io_on.flags = MAP_ACTIVE | IO_DATA_PATH_WIDTH_8;
+                       io_on.start = s->io[i].res->start;
+                       io_on.stop = s->io[i].res->end;
+
+                       s->ops->set_io_map(s, &io_off);
+                       mdelay(40);
+                       s->ops->set_io_map(s, &io_on);
+               }
+       }
+
        return CS_SUCCESS;
 } /* modify_configuration */
 EXPORT_SYMBOL(pcmcia_modify_configuration);
@@ -463,23 +473,23 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
 {
        pccard_io_map io = { 0, 0, 0, 0, 1 };
        struct pcmcia_socket *s = p_dev->socket;
+       config_t *c = p_dev->function_config;
        int i;
 
-       if (!(p_dev->state & CLIENT_CONFIG_LOCKED))
-               return CS_BAD_HANDLE;
-       p_dev->state &= ~CLIENT_CONFIG_LOCKED;
-
-       if (!(p_dev->state & CLIENT_STALE)) {
-               config_t *c = CONFIG(p_dev);
+       if (p_dev->_locked) {
+               p_dev->_locked = 0;
                if (--(s->lock_count) == 0) {
                        s->socket.flags = SS_OUTPUT_ENA;   /* Is this correct? */
                        s->socket.Vpp = 0;
                        s->socket.io_irq = 0;
                        s->ops->set_socket(s, &s->socket);
                }
+       }
+       if (c->state & CONFIG_LOCKED) {
+               c->state &= ~CONFIG_LOCKED;
                if (c->state & CONFIG_IO_REQ)
                        for (i = 0; i < MAX_IO_WIN; i++) {
-                               if (s->io[i].NumPorts == 0)
+                               if (!s->io[i].res)
                                        continue;
                                s->io[i].Config--;
                                if (s->io[i].Config != 0)
@@ -487,12 +497,10 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
                                io.map = i;
                                s->ops->set_io_map(s, &io);
                        }
-               c->state &= ~CONFIG_LOCKED;
        }
 
        return CS_SUCCESS;
 } /* pcmcia_release_configuration */
-EXPORT_SYMBOL(pcmcia_release_configuration);
 
 
 /** pcmcia_release_io
@@ -503,25 +511,23 @@ EXPORT_SYMBOL(pcmcia_release_configuration);
  * don't bother checking the port ranges against the current socket
  * values.
  */
-int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
+static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
 {
        struct pcmcia_socket *s = p_dev->socket;
+       config_t *c = p_dev->function_config;
 
-       if (!(p_dev->state & CLIENT_IO_REQ))
+       if (!p_dev->_io )
                return CS_BAD_HANDLE;
-       p_dev->state &= ~CLIENT_IO_REQ;
-
-       if (!(p_dev->state & CLIENT_STALE)) {
-               config_t *c = CONFIG(p_dev);
-               if (c->state & CONFIG_LOCKED)
-                       return CS_CONFIGURATION_LOCKED;
-               if ((c->io.BasePort1 != req->BasePort1) ||
-                   (c->io.NumPorts1 != req->NumPorts1) ||
-                   (c->io.BasePort2 != req->BasePort2) ||
-                   (c->io.NumPorts2 != req->NumPorts2))
-                       return CS_BAD_ARGS;
-               c->state &= ~CONFIG_IO_REQ;
-       }
+
+       p_dev->_io = 0;
+
+       if ((c->io.BasePort1 != req->BasePort1) ||
+           (c->io.NumPorts1 != req->NumPorts1) ||
+           (c->io.BasePort2 != req->BasePort2) ||
+           (c->io.NumPorts2 != req->NumPorts2))
+               return CS_BAD_ARGS;
+
+       c->state &= ~CONFIG_IO_REQ;
 
        release_io_space(s, req->BasePort1, req->NumPorts1);
        if (req->NumPorts2)
@@ -529,28 +535,26 @@ int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
 
        return CS_SUCCESS;
 } /* pcmcia_release_io */
-EXPORT_SYMBOL(pcmcia_release_io);
 
 
-int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
+static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 {
        struct pcmcia_socket *s = p_dev->socket;
-       if (!(p_dev->state & CLIENT_IRQ_REQ))
+       config_t *c= p_dev->function_config;
+
+       if (!p_dev->_irq)
                return CS_BAD_HANDLE;
-       p_dev->state &= ~CLIENT_IRQ_REQ;
-
-       if (!(p_dev->state & CLIENT_STALE)) {
-               config_t *c = CONFIG(p_dev);
-               if (c->state & CONFIG_LOCKED)
-                       return CS_CONFIGURATION_LOCKED;
-               if (c->irq.Attributes != req->Attributes)
-                       return CS_BAD_ATTRIBUTE;
-               if (s->irq.AssignedIRQ != req->AssignedIRQ)
-                       return CS_BAD_IRQ;
-               if (--s->irq.Config == 0) {
-                       c->state &= ~CONFIG_IRQ_REQ;
-                       s->irq.AssignedIRQ = 0;
-               }
+       p_dev->_irq = 0;
+
+       if (c->state & CONFIG_LOCKED)
+               return CS_CONFIGURATION_LOCKED;
+       if (c->irq.Attributes != req->Attributes)
+               return CS_BAD_ATTRIBUTE;
+       if (s->irq.AssignedIRQ != req->AssignedIRQ)
+               return CS_BAD_IRQ;
+       if (--s->irq.Config == 0) {
+               c->state &= ~CONFIG_IRQ_REQ;
+               s->irq.AssignedIRQ = 0;
        }
 
        if (req->Attributes & IRQ_HANDLE_PRESENT) {
@@ -563,7 +567,6 @@ int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 
        return CS_SUCCESS;
 } /* pcmcia_release_irq */
-EXPORT_SYMBOL(pcmcia_release_irq);
 
 
 int pcmcia_release_window(window_handle_t win)
@@ -573,7 +576,7 @@ int pcmcia_release_window(window_handle_t win)
        if ((win == NULL) || (win->magic != WINDOW_MAGIC))
                return CS_BAD_HANDLE;
        s = win->sock;
-       if (!(win->handle->state & CLIENT_WIN_REQ(win->index)))
+       if (!(win->handle->_win & CLIENT_WIN_REQ(win->index)))
                return CS_BAD_HANDLE;
 
        /* Shut down memory window */
@@ -587,7 +590,7 @@ int pcmcia_release_window(window_handle_t win)
                kfree(win->ctl.res);
                win->ctl.res = NULL;
        }
-       win->handle->state &= ~CLIENT_WIN_REQ(win->index);
+       win->handle->_win &= ~CLIENT_WIN_REQ(win->index);
 
        win->magic = 0;
 
@@ -610,16 +613,12 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 
        if (req->IntType & INT_CARDBUS)
                return CS_UNSUPPORTED_MODE;
-       c = CONFIG(p_dev);
+       c = p_dev->function_config;
        if (c->state & CONFIG_LOCKED)
                return CS_CONFIGURATION_LOCKED;
 
        /* Do power control.  We don't allow changes in Vcc. */
-       if (s->socket.Vcc != req->Vcc)
-               return CS_BAD_VCC;
-       if (req->Vpp1 != req->Vpp2)
-               return CS_BAD_VPP;
-       s->socket.Vpp = req->Vpp1;
+       s->socket.Vpp = req->Vpp;
        if (s->ops->set_socket(s, &s->socket))
                return CS_BAD_VPP;
 
@@ -643,7 +642,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 
        /* Set up CIS configuration registers */
        base = c->ConfigBase = req->ConfigBase;
-       c->Present = c->CardValues = req->Present;
+       c->CardValues = req->Present;
        if (req->Present & PRESENT_COPY) {
                c->Copy = req->Copy;
                pcmcia_write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &c->Copy);
@@ -690,10 +689,10 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
        if (c->state & CONFIG_IO_REQ) {
                iomap.speed = io_speed;
                for (i = 0; i < MAX_IO_WIN; i++)
-                       if (s->io[i].NumPorts != 0) {
+                       if (s->io[i].res) {
                                iomap.map = i;
                                iomap.flags = MAP_ACTIVE;
-                               switch (s->io[i].Attributes & IO_DATA_PATH_WIDTH) {
+                               switch (s->io[i].res->flags & IO_DATA_PATH_WIDTH) {
                                case IO_DATA_PATH_WIDTH_16:
                                        iomap.flags |= MAP_16BIT; break;
                                case IO_DATA_PATH_WIDTH_AUTO:
@@ -701,15 +700,15 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
                                default:
                                        break;
                                }
-                               iomap.start = s->io[i].BasePort;
-                               iomap.stop = iomap.start + s->io[i].NumPorts - 1;
+                               iomap.start = s->io[i].res->start;
+                               iomap.stop = s->io[i].res->end;
                                s->ops->set_io_map(s, &iomap);
                                s->io[i].Config++;
                        }
        }
 
        c->state |= CONFIG_LOCKED;
-       p_dev->state |= CLIENT_CONFIG_LOCKED;
+       p_dev->_locked = 1;
        return CS_SUCCESS;
 } /* pcmcia_request_configuration */
 EXPORT_SYMBOL(pcmcia_request_configuration);
@@ -730,7 +729,7 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req)
 
        if (!req)
                return CS_UNSUPPORTED_MODE;
-       c = CONFIG(p_dev);
+       c = p_dev->function_config;
        if (c->state & CONFIG_LOCKED)
                return CS_CONFIGURATION_LOCKED;
        if (c->state & CONFIG_IO_REQ)
@@ -755,7 +754,7 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req)
 
        c->io = *req;
        c->state |= CONFIG_IO_REQ;
-       p_dev->state |= CLIENT_IO_REQ;
+       p_dev->_io = 1;
        return CS_SUCCESS;
 } /* pcmcia_request_io */
 EXPORT_SYMBOL(pcmcia_request_io);
@@ -786,7 +785,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 
        if (!(s->state & SOCKET_PRESENT))
                return CS_NO_CARD;
-       c = CONFIG(p_dev);
+       c = p_dev->function_config;
        if (c->state & CONFIG_LOCKED)
                return CS_CONFIGURATION_LOCKED;
        if (c->state & CONFIG_IRQ_REQ)
@@ -851,7 +850,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
        s->irq.Config++;
 
        c->state |= CONFIG_IRQ_REQ;
-       p_dev->state |= CLIENT_IRQ_REQ;
+       p_dev->_irq = 1;
 
 #ifdef CONFIG_PCMCIA_PROBE
        pcmcia_used_irq[irq]++;
@@ -911,7 +910,7 @@ int pcmcia_request_window(struct pcmcia_device **p_dev, win_req_t *req, window_h
                if (!win->ctl.res)
                        return CS_IN_USE;
        }
-       (*p_dev)->state |= CLIENT_WIN_REQ(w);
+       (*p_dev)->_win |= CLIENT_WIN_REQ(w);
 
        /* Configure the socket controller */
        win->ctl.map = w+1;
@@ -941,3 +940,14 @@ int pcmcia_request_window(struct pcmcia_device **p_dev, win_req_t *req, window_h
        return CS_SUCCESS;
 } /* pcmcia_request_window */
 EXPORT_SYMBOL(pcmcia_request_window);
+
+void pcmcia_disable_device(struct pcmcia_device *p_dev) {
+       pcmcia_release_configuration(p_dev);
+       pcmcia_release_io(p_dev, &p_dev->io);
+       pcmcia_release_irq(p_dev, &p_dev->irq);
+       if (&p_dev->win)
+               pcmcia_release_window(p_dev->win);
+
+       p_dev->dev_node = NULL;
+}
+EXPORT_SYMBOL(pcmcia_disable_device);
index f2789af..16d1ea7 100644 (file)
@@ -8,7 +8,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/init.h>
index 5146093..81dfc2c 100644 (file)
@@ -12,7 +12,6 @@
  * (C) 1999            David A. Hinds
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 
@@ -22,6 +21,8 @@
 #include "cs_internal.h"
 
 
+#ifdef CONFIG_PCMCIA_IOCTL
+
 #ifdef CONFIG_PCMCIA_PROBE
 
 static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
@@ -98,6 +99,8 @@ int pcmcia_adjust_resource_info(adjust_t *adj)
 }
 EXPORT_SYMBOL(pcmcia_adjust_resource_info);
 
+#endif
+
 int pcmcia_validate_mem(struct pcmcia_socket *s)
 {
        if (s->resource_ops->validate_mem)
index 5301ac6..0f8b157 100644 (file)
@@ -12,7 +12,6 @@
  * (C) 1999            David A. Hinds
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
@@ -61,7 +60,7 @@ struct socket_data {
        unsigned int                    rsrc_mem_probe;
 };
 
-static DECLARE_MUTEX(rsrc_sem);
+static DEFINE_MUTEX(rsrc_mutex);
 #define MEM_PROBE_LOW  (1 << 0)
 #define MEM_PROBE_HIGH (1 << 1)
 
@@ -484,7 +483,7 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 
 
 /*
- * Locking note: Must be called with skt_sem held!
+ * Locking note: Must be called with skt_mutex held!
  */
 static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
 {
@@ -495,7 +494,7 @@ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
        if (!probe_mem)
                return 0;
 
-       down(&rsrc_sem);
+       mutex_lock(&rsrc_mutex);
 
        if (s->features & SS_CAP_PAGE_REGS)
                probe_mask = MEM_PROBE_HIGH;
@@ -507,7 +506,7 @@ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
                        s_data->rsrc_mem_probe |= probe_mask;
        }
 
-       up(&rsrc_sem);
+       mutex_unlock(&rsrc_mutex);
 
        return ret;
 }
@@ -585,7 +584,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star
        struct socket_data *s_data = s->resource_data;
        int ret = -ENOMEM;
 
-       down(&rsrc_sem);
+       mutex_lock(&rsrc_mutex);
        for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
                unsigned long start = m->base;
                unsigned long end = m->base + m->num - 1;
@@ -596,7 +595,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star
                ret = adjust_resource(res, r_start, r_end - r_start + 1);
                break;
        }
-       up(&rsrc_sem);
+       mutex_unlock(&rsrc_mutex);
 
        return ret;
 }
@@ -630,7 +629,7 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num,
        data.offset = base & data.mask;
        data.map = &s_data->io_db;
 
-       down(&rsrc_sem);
+       mutex_lock(&rsrc_mutex);
 #ifdef CONFIG_PCI
        if (s->cb_dev) {
                ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
@@ -639,7 +638,7 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num,
 #endif
                ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
                                        1, pcmcia_align, &data);
-       up(&rsrc_sem);
+       mutex_unlock(&rsrc_mutex);
 
        if (ret != 0) {
                kfree(res);
@@ -672,7 +671,7 @@ static struct resource * nonstatic_find_mem_region(u_long base, u_long num,
                        min = 0x100000UL + base;
                }
 
-               down(&rsrc_sem);
+               mutex_lock(&rsrc_mutex);
 #ifdef CONFIG_PCI
                if (s->cb_dev) {
                        ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
@@ -682,7 +681,7 @@ static struct resource * nonstatic_find_mem_region(u_long base, u_long num,
 #endif
                        ret = allocate_resource(&iomem_resource, res, num, min,
                                                max, 1, pcmcia_align, &data);
-               up(&rsrc_sem);
+               mutex_unlock(&rsrc_mutex);
                if (ret == 0 || low)
                        break;
                low = 1;
@@ -705,7 +704,7 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned
        if (end < start)
                return -EINVAL;
 
-       down(&rsrc_sem);
+       mutex_lock(&rsrc_mutex);
        switch (action) {
        case ADD_MANAGED_RESOURCE:
                ret = add_interval(&data->mem_db, start, size);
@@ -723,7 +722,7 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned
        default:
                ret = -EINVAL;
        }
-       up(&rsrc_sem);
+       mutex_unlock(&rsrc_mutex);
 
        return ret;
 }
@@ -741,7 +740,7 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long
        if (end > IO_SPACE_LIMIT)
                return -EINVAL;
 
-       down(&rsrc_sem);
+       mutex_lock(&rsrc_mutex);
        switch (action) {
        case ADD_MANAGED_RESOURCE:
                if (add_interval(&data->io_db, start, size) != 0) {
@@ -760,7 +759,7 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long
                ret = -EINVAL;
                break;
        }
-       up(&rsrc_sem);
+       mutex_unlock(&rsrc_mutex);
 
        return ret;
 }
@@ -867,7 +866,7 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s)
        struct socket_data *data = s->resource_data;
        struct resource_map *p, *q;
 
-       down(&rsrc_sem);
+       mutex_lock(&rsrc_mutex);
        for (p = data->mem_db.next; p != &data->mem_db; p = q) {
                q = p->next;
                kfree(p);
@@ -876,7 +875,7 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s)
                q = p->next;
                kfree(p);
        }
-       up(&rsrc_sem);
+       mutex_unlock(&rsrc_mutex);
 }
 
 
@@ -901,7 +900,7 @@ static ssize_t show_io_db(struct class_device *class_dev, char *buf)
        struct resource_map *p;
        ssize_t ret = 0;
 
-       down(&rsrc_sem);
+       mutex_lock(&rsrc_mutex);
        data = s->resource_data;
 
        for (p = data->io_db.next; p != &data->io_db; p = p->next) {
@@ -913,7 +912,7 @@ static ssize_t show_io_db(struct class_device *class_dev, char *buf)
                                 ((unsigned long) p->base + p->num - 1));
        }
 
-       up(&rsrc_sem);
+       mutex_unlock(&rsrc_mutex);
        return (ret);
 }
 
@@ -953,7 +952,7 @@ static ssize_t show_mem_db(struct class_device *class_dev, char *buf)
        struct resource_map *p;
        ssize_t ret = 0;
 
-       down(&rsrc_sem);
+       mutex_lock(&rsrc_mutex);
        data = s->resource_data;
 
        for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
@@ -965,7 +964,7 @@ static ssize_t show_mem_db(struct class_device *class_dev, char *buf)
                                 ((unsigned long) p->base + p->num - 1));
        }
 
-       up(&rsrc_sem);
+       mutex_unlock(&rsrc_mutex);
        return (ret);
 }
 
index 2b3c289..eb89928 100644 (file)
@@ -5,7 +5,6 @@
  * Based off the Assabet.
  *
  */
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
index 5ab1cde..c5d7476 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/config.h>
 #include <linux/string.h>
 #include <linux/major.h>
 #include <linux/errno.h>
@@ -25,6 +24,7 @@
 #include <linux/pm.h>
 #include <linux/pci.h>
 #include <linux/device.h>
+#include <linux/mutex.h>
 #include <asm/system.h>
 #include <asm/irq.h>
 
@@ -183,7 +183,7 @@ static ssize_t pccard_store_resource(struct class_device *dev, const char *buf,
                s->resource_setup_done = 1;
        spin_unlock_irqrestore(&s->lock, flags);
 
-       down(&s->skt_sem);
+       mutex_lock(&s->skt_mutex);
        if ((s->callback) &&
            (s->state & SOCKET_PRESENT) &&
            !(s->state & SOCKET_CARDBUS)) {
@@ -192,7 +192,7 @@ static ssize_t pccard_store_resource(struct class_device *dev, const char *buf,
                        module_put(s->callback->owner);
                }
        }
-       up(&s->skt_sem);
+       mutex_unlock(&s->skt_mutex);
 
        return count;
 }
@@ -322,7 +322,7 @@ static ssize_t pccard_store_cis(struct kobject *kobj, char *buf, loff_t off, siz
        kfree(cis);
 
        if (!ret) {
-               down(&s->skt_sem);
+               mutex_lock(&s->skt_mutex);
                if ((s->callback) && (s->state & SOCKET_PRESENT) &&
                    !(s->state & SOCKET_CARDBUS)) {
                        if (try_module_get(s->callback->owner)) {
@@ -330,7 +330,7 @@ static ssize_t pccard_store_cis(struct kobject *kobj, char *buf, loff_t off, siz
                                module_put(s->callback->owner);
                        }
                }
-               up(&s->skt_sem);
+               mutex_unlock(&s->skt_mutex);
        }
 
 
index d5b4ff7..7a3d1b8 100644 (file)
@@ -30,7 +30,6 @@
 #ifndef _LINUX_TI113X_H
 #define _LINUX_TI113X_H
 
-#include <linux/config.h>
 
 /* Register definitions for TI 113X PCI-to-CardBus bridges */
 
index 8fd71ab..b842377 100644 (file)
@@ -32,9 +32,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
        int size;
 
        /* Sanity checks */
-       if ( magic == NULL || datasize > PAGE_SIZE ||
-            (cplength*sizeof(struct ccw1)) > PAGE_SIZE)
-               BUG();
+       BUG_ON( magic == NULL || datasize > PAGE_SIZE ||
+            (cplength*sizeof(struct ccw1)) > PAGE_SIZE);
 
        size = (sizeof(struct dasd_ccw_req) + 7L) & -8L;
        if (cplength > 0)
@@ -125,8 +124,7 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
        struct dasd_device *device;
        int success;
 
-       if (cqr->refers == NULL || cqr->function == NULL)
-               BUG();
+       BUG_ON(cqr->refers == NULL || cqr->function == NULL);
 
        device = cqr->device;
        success = cqr->status == DASD_CQR_DONE;
index ac10dfb..91e93c7 100644 (file)
@@ -24,7 +24,7 @@
 
 /*
  * The room for the SCCB (only for writing) is not equal to a pages size
- * (as it is specified as the maximum size in the the SCLP ducumentation)
+ * (as it is specified as the maximum size in the the SCLP documentation)
  * because of the additional data structure described above.
  */
 #define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer))
index 5ced272..5c65cf3 100644 (file)
@@ -198,9 +198,7 @@ tapeblock_request_fn(request_queue_t *queue)
 
        device = (struct tape_device *) queue->queuedata;
        DBF_LH(6, "tapeblock_request_fn(device=%p)\n", device);
-       if (device == NULL)
-               BUG();
-
+       BUG_ON(device == NULL);
        tapeblock_trigger_requeue(device);
 }
 
@@ -307,8 +305,7 @@ tapeblock_revalidate_disk(struct gendisk *disk)
        int                     rc;
 
        device = (struct tape_device *) disk->private_data;
-       if (!device)
-               BUG();
+       BUG_ON(!device);
 
        if (!device->blk_data.medium_changed)
                return 0;
@@ -440,11 +437,9 @@ tapeblock_ioctl(
 
        rc     = 0;
        disk   = inode->i_bdev->bd_disk;
-       if (!disk)
-               BUG();
+       BUG_ON(!disk);
        device = disk->private_data;
-       if (!device)
-               BUG();
+       BUG_ON(!device);
        minor  = iminor(inode);
 
        DBF_LH(6, "tapeblock_ioctl(0x%0x)\n", command);
index edcf05d..5d6b7a5 100644 (file)
@@ -675,9 +675,8 @@ lcs_ready_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
        int index, rc;
 
        LCS_DBF_TEXT(5, trace, "rdybuff");
-       if (buffer->state != BUF_STATE_LOCKED &&
-           buffer->state != BUF_STATE_PROCESSED)
-               BUG();
+       BUG_ON(buffer->state != BUF_STATE_LOCKED &&
+               buffer->state != BUF_STATE_PROCESSED);
        spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
        buffer->state = BUF_STATE_READY;
        index = buffer - channel->iob;
@@ -701,8 +700,7 @@ __lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
        int index, prev, next;
 
        LCS_DBF_TEXT(5, trace, "prcsbuff");
-       if (buffer->state != BUF_STATE_READY)
-               BUG();
+       BUG_ON(buffer->state != BUF_STATE_READY);
        buffer->state = BUF_STATE_PROCESSED;
        index = buffer - channel->iob;
        prev = (index - 1) & (LCS_NUM_BUFFS - 1);
@@ -734,9 +732,8 @@ lcs_release_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
        unsigned long flags;
 
        LCS_DBF_TEXT(5, trace, "relbuff");
-       if (buffer->state != BUF_STATE_LOCKED &&
-           buffer->state != BUF_STATE_PROCESSED)
-               BUG();
+       BUG_ON(buffer->state != BUF_STATE_LOCKED &&
+               buffer->state != BUF_STATE_PROCESSED);
        spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
        buffer->state = BUF_STATE_EMPTY;
        spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
index 6c2c395..5517da5 100644 (file)
@@ -86,7 +86,7 @@ config AIC7XXX_DEBUG_MASK
         default "0"
         help
        Bit mask of debug options that is only valid if the
-       CONFIG_AIC7XXX_DEBUG_ENBLE option is enabled.  The bits in this mask
+       CONFIG_AIC7XXX_DEBUG_ENABLE option is enabled.  The bits in this mask
        are defined in the drivers/scsi/aic7xxx/aic7xxx.h - search for the
        variable ahc_debug in that file to find them.
 
index f47dd87..892e8ed 100644 (file)
@@ -80,7 +80,7 @@ void ibmvscsi_release_crq_queue(struct crq_queue *queue,
        tasklet_kill(&hostdata->srp_task);
        do {
                rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
-       } while ((rc == H_Busy) || (H_isLongBusy(rc)));
+       } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
        dma_unmap_single(hostdata->dev,
                         queue->msg_token,
                         queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
@@ -230,7 +230,7 @@ int ibmvscsi_init_crq_queue(struct crq_queue *queue,
        rc = plpar_hcall_norets(H_REG_CRQ,
                                vdev->unit_address,
                                queue->msg_token, PAGE_SIZE);
-       if (rc == H_Resource) 
+       if (rc == H_RESOURCE)
                /* maybe kexecing and resource is busy. try a reset */
                rc = ibmvscsi_reset_crq_queue(queue,
                                              hostdata);
@@ -269,7 +269,7 @@ int ibmvscsi_init_crq_queue(struct crq_queue *queue,
       req_irq_failed:
        do {
                rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
-       } while ((rc == H_Busy) || (H_isLongBusy(rc)));
+       } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
       reg_crq_failed:
        dma_unmap_single(hostdata->dev,
                         queue->msg_token,
@@ -295,7 +295,7 @@ int ibmvscsi_reenable_crq_queue(struct crq_queue *queue,
        /* Re-enable the CRQ */
        do {
                rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
-       } while ((rc == H_InProgress) || (rc == H_Busy) || (H_isLongBusy(rc)));
+       } while ((rc == H_IN_PROGRESS) || (rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
 
        if (rc)
                printk(KERN_ERR "ibmvscsi: Error %d enabling adapter\n", rc);
@@ -317,7 +317,7 @@ int ibmvscsi_reset_crq_queue(struct crq_queue *queue,
        /* Close the CRQ */
        do {
                rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
-       } while ((rc == H_Busy) || (H_isLongBusy(rc)));
+       } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
 
        /* Clean out the queue */
        memset(queue->msgs, 0x00, PAGE_SIZE);
index 5609847..ee449b2 100644 (file)
@@ -89,29 +89,29 @@ MODULE_LICENSE("Dual MPL/GPL");
 /*====================================================================*/
 
 typedef struct scsi_info_t {
-    dev_link_t         link;
+       struct pcmcia_device    *p_dev;
     dev_node_t         node;
     struct Scsi_Host   *host;
 } scsi_info_t;
 
-static void aha152x_release_cs(dev_link_t *link);
+static void aha152x_release_cs(struct pcmcia_device *link);
 static void aha152x_detach(struct pcmcia_device *p_dev);
-static void aha152x_config_cs(dev_link_t *link);
+static int aha152x_config_cs(struct pcmcia_device *link);
 
-static dev_link_t *dev_list;
+static struct pcmcia_device *dev_list;
 
-static int aha152x_attach(struct pcmcia_device *p_dev)
+static int aha152x_probe(struct pcmcia_device *link)
 {
     scsi_info_t *info;
-    dev_link_t *link;
-    
+
     DEBUG(0, "aha152x_attach()\n");
 
     /* Create new SCSI device */
     info = kmalloc(sizeof(*info), GFP_KERNEL);
     if (!info) return -ENOMEM;
     memset(info, 0, sizeof(*info));
-    link = &info->link; link->priv = info;
+    info->p_dev = link;
+    link->priv = info;
 
     link->io.NumPorts1 = 0x20;
     link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
@@ -119,41 +119,22 @@ static int aha152x_attach(struct pcmcia_device *p_dev)
     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
     link->irq.IRQInfo1 = IRQ_LEVEL_ID;
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     link->conf.IntType = INT_MEMORY_AND_IO;
     link->conf.Present = PRESENT_OPTION;
 
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    aha152x_config_cs(link);
-
-    return 0;
+    return aha152x_config_cs(link);
 } /* aha152x_attach */
 
 /*====================================================================*/
 
-static void aha152x_detach(struct pcmcia_device *p_dev)
+static void aha152x_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-    dev_link_t **linkp;
-
     DEBUG(0, "aha152x_detach(0x%p)\n", link);
-    
-    /* Locate device structure */
-    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
-       if (*linkp == link) break;
-    if (*linkp == NULL)
-       return;
 
-    if (link->state & DEV_CONFIG)
-       aha152x_release_cs(link);
+    aha152x_release_cs(link);
 
     /* Unlink device structure, free bits */
-    *linkp = link->next;
     kfree(link->priv);
-    
 } /* aha152x_detach */
 
 /*====================================================================*/
@@ -161,9 +142,8 @@ static void aha152x_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void aha152x_config_cs(dev_link_t *link)
+static int aha152x_config_cs(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     scsi_info_t *info = link->priv;
     struct aha152x_setup s;
     tuple_t tuple;
@@ -178,19 +158,16 @@ static void aha152x_config_cs(dev_link_t *link)
     tuple.TupleData = tuple_data;
     tuple.TupleDataMax = 64;
     tuple.TupleOffset = 0;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     while (1) {
-       if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+       if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+               pcmcia_parse_tuple(link, &tuple, &parse) != 0)
            goto next_entry;
        /* For New Media T&J, look for a SCSI window */
        if (parse.cftable_entry.io.win[0].len >= 0x20)
@@ -201,15 +178,15 @@ static void aha152x_config_cs(dev_link_t *link)
        if ((parse.cftable_entry.io.nwin > 0) &&
            (link->io.BasePort1 < 0xffff)) {
            link->conf.ConfigIndex = parse.cftable_entry.index;
-           i = pcmcia_request_io(handle, &link->io);
+           i = pcmcia_request_io(link, &link->io);
            if (i == CS_SUCCESS) break;
        }
     next_entry:
-       CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+       CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
     }
     
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
     
     /* Set configuration options for the aha152x driver */
     memset(&s, 0, sizeof(s));
@@ -231,53 +208,30 @@ static void aha152x_config_cs(dev_link_t *link)
     }
 
     sprintf(info->node.dev_name, "scsi%d", host->host_no);
-    link->dev = &info->node;
+    link->dev_node = &info->node;
     info->host = host;
 
-    link->state &= ~DEV_CONFIG_PENDING;
-    return;
-    
+    return 0;
+
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
     aha152x_release_cs(link);
-    return;
+    return -ENODEV;
 }
 
-static void aha152x_release_cs(dev_link_t *link)
+static void aha152x_release_cs(struct pcmcia_device *link)
 {
        scsi_info_t *info = link->priv;
 
        aha152x_release(info->host);
-       link->dev = NULL;
-    
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
-    
-       link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
 }
 
-static int aha152x_suspend(struct pcmcia_device *dev)
+static int aha152x_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int aha152x_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
        scsi_info_t *info = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               aha152x_host_reset_host(info->host);
-       }
+       aha152x_host_reset_host(info->host);
 
        return 0;
 }
@@ -297,10 +251,9 @@ static struct pcmcia_driver aha152x_cs_driver = {
        .drv            = {
                .name   = "aha152x_cs",
        },
-       .probe          = aha152x_attach,
+       .probe          = aha152x_probe,
        .remove         = aha152x_detach,
        .id_table       = aha152x_ids,
-       .suspend        = aha152x_suspend,
        .resume         = aha152x_resume,
 };
 
@@ -317,4 +270,3 @@ static void __exit exit_aha152x_cs(void)
 
 module_init(init_aha152x_cs);
 module_exit(exit_aha152x_cs);
index 788c58d..85f7ffa 100644 (file)
@@ -73,57 +73,48 @@ static char *version =
 /*====================================================================*/
 
 typedef struct scsi_info_t {
-    dev_link_t         link;
+       struct pcmcia_device    *p_dev;
     dev_node_t         node;
     struct Scsi_Host   *host;
 } scsi_info_t;
 
 
-static void fdomain_release(dev_link_t *link);
+static void fdomain_release(struct pcmcia_device *link);
 static void fdomain_detach(struct pcmcia_device *p_dev);
-static void fdomain_config(dev_link_t *link);
+static int fdomain_config(struct pcmcia_device *link);
 
-static int fdomain_attach(struct pcmcia_device *p_dev)
+static int fdomain_probe(struct pcmcia_device *link)
 {
-    scsi_info_t *info;
-    dev_link_t *link;
-
-    DEBUG(0, "fdomain_attach()\n");
-
-    /* Create new SCSI device */
-    info = kmalloc(sizeof(*info), GFP_KERNEL);
-    if (!info) return -ENOMEM;
-    memset(info, 0, sizeof(*info));
-    link = &info->link; link->priv = info;
-    link->io.NumPorts1 = 0x10;
-    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
-    link->io.IOAddrLines = 10;
-    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
-    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
-    link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
-    link->conf.IntType = INT_MEMORY_AND_IO;
-    link->conf.Present = PRESENT_OPTION;
-
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    fdomain_config(link);
-
-    return 0;
+       scsi_info_t *info;
+
+       DEBUG(0, "fdomain_attach()\n");
+
+       /* Create new SCSI device */
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->p_dev = link;
+       link->priv = info;
+       link->io.NumPorts1 = 0x10;
+       link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+       link->io.IOAddrLines = 10;
+       link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+       link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+       link->conf.Attributes = CONF_ENABLE_IRQ;
+       link->conf.IntType = INT_MEMORY_AND_IO;
+       link->conf.Present = PRESENT_OPTION;
+
+       return fdomain_config(link);
 } /* fdomain_attach */
 
 /*====================================================================*/
 
-static void fdomain_detach(struct pcmcia_device *p_dev)
+static void fdomain_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
-
        DEBUG(0, "fdomain_detach(0x%p)\n", link);
 
-       if (link->state & DEV_CONFIG)
-               fdomain_release(link);
+       fdomain_release(link);
 
        kfree(link->priv);
 } /* fdomain_detach */
@@ -133,9 +124,8 @@ static void fdomain_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void fdomain_config(dev_link_t *link)
+static int fdomain_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
     scsi_info_t *info = link->priv;
     tuple_t tuple;
     cisparse_t parse;
@@ -150,103 +140,75 @@ static void fdomain_config(dev_link_t *link)
     tuple.TupleData = tuple_data;
     tuple.TupleDataMax = 64;
     tuple.TupleOffset = 0;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
     link->conf.ConfigBase = parse.config.base;
 
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-    
     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
     while (1) {
-       if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+       if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+               pcmcia_parse_tuple(link, &tuple, &parse) != 0)
            goto next_entry;
        link->conf.ConfigIndex = parse.cftable_entry.index;
        link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
-       i = pcmcia_request_io(handle, &link->io);
+       i = pcmcia_request_io(link, &link->io);
        if (i == CS_SUCCESS) break;
     next_entry:
-       CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+       CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
     }
 
-    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
-    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
-    
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+
     /* A bad hack... */
     release_region(link->io.BasePort1, link->io.NumPorts1);
 
     /* Set configuration options for the fdomain driver */
     sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ);
     fdomain_setup(str);
-    
+
     host = __fdomain_16x0_detect(&fdomain_driver_template);
     if (!host) {
         printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
        goto cs_failed;
     }
-    scsi_add_host(host, NULL); /* XXX handle failure */
+
+    if (scsi_add_host(host, NULL))
+           goto cs_failed;
     scsi_scan_host(host);
 
     sprintf(info->node.dev_name, "scsi%d", host->host_no);
-    link->dev = &info->node;
+    link->dev_node = &info->node;
     info->host = host;
-    
-    link->state &= ~DEV_CONFIG_PENDING;
-    return;
-    
+
+    return 0;
+
 cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+    cs_error(link, last_fn, last_ret);
     fdomain_release(link);
-    return;
-    
+    return -ENODEV;
 } /* fdomain_config */
 
 /*====================================================================*/
 
-static void fdomain_release(dev_link_t *link)
+static void fdomain_release(struct pcmcia_device *link)
 {
-    scsi_info_t *info = link->priv;
+       scsi_info_t *info = link->priv;
 
-    DEBUG(0, "fdomain_release(0x%p)\n", link);
+       DEBUG(0, "fdomain_release(0x%p)\n", link);
 
-    scsi_remove_host(info->host);
-    link->dev = NULL;
-    
-    pcmcia_release_configuration(link->handle);
-    pcmcia_release_io(link->handle, &link->io);
-    pcmcia_release_irq(link->handle, &link->irq);
-
-    scsi_unregister(info->host);
-
-    link->state &= ~DEV_CONFIG;
+       scsi_remove_host(info->host);
+       pcmcia_disable_device(link);
+       scsi_unregister(info->host);
 }
 
 /*====================================================================*/
 
-static int fdomain_suspend(struct pcmcia_device *dev)
+static int fdomain_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int fdomain_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-               fdomain_16x0_bus_reset(NULL);
-       }
+       fdomain_16x0_bus_reset(NULL);
 
        return 0;
 }
@@ -264,10 +226,9 @@ static struct pcmcia_driver fdomain_cs_driver = {
        .drv            = {
                .name   = "fdomain_cs",
        },
-       .probe          = fdomain_attach,
+       .probe          = fdomain_probe,
        .remove         = fdomain_detach,
        .id_table       = fdomain_ids,
-       .suspend        = fdomain_suspend,
        .resume         = fdomain_resume,
 };
 
index 9e3ab3f..231f9c3 100644 (file)
@@ -1593,11 +1593,11 @@ static int nsp_eh_host_reset(Scsi_Cmnd *SCpnt)
     configure the card at this point -- we wait until we receive a
     card insertion event.
 ======================================================================*/
-static int nsp_cs_attach(struct pcmcia_device *p_dev)
+static int nsp_cs_probe(struct pcmcia_device *link)
 {
        scsi_info_t  *info;
-       dev_link_t   *link;
        nsp_hw_data  *data = &nsp_data_base;
+       int ret;
 
        nsp_dbg(NSP_DEBUG_INIT, "in");
 
@@ -1605,7 +1605,7 @@ static int nsp_cs_attach(struct pcmcia_device *p_dev)
        info = kmalloc(sizeof(*info), GFP_KERNEL);
        if (info == NULL) { return -ENOMEM; }
        memset(info, 0, sizeof(*info));
-       link = &info->link;
+       info->p_dev = link;
        link->priv = info;
        data->ScsiInfo = info;
 
@@ -1627,18 +1627,13 @@ static int nsp_cs_attach(struct pcmcia_device *p_dev)
 
        /* General socket configuration */
        link->conf.Attributes    = CONF_ENABLE_IRQ;
-       link->conf.Vcc           = 50;
        link->conf.IntType       = INT_MEMORY_AND_IO;
        link->conf.Present       = PRESENT_OPTION;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       nsp_cs_config(link);
+       ret = nsp_cs_config(link);
 
        nsp_dbg(NSP_DEBUG_INIT, "link=0x%p", link);
-       return 0;
+       return ret;
 } /* nsp_cs_attach */
 
 
@@ -1648,16 +1643,12 @@ static int nsp_cs_attach(struct pcmcia_device *p_dev)
     structures are freed.  Otherwise, the structures will be freed
     when the device is released.
 ======================================================================*/
-static void nsp_cs_detach(struct pcmcia_device *p_dev)
+static void nsp_cs_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
-
        nsp_dbg(NSP_DEBUG_INIT, "in, link=0x%p", link);
 
-       if (link->state & DEV_CONFIG) {
-               ((scsi_info_t *)link->priv)->stop = 1;
-               nsp_cs_release(link);
-       }
+       ((scsi_info_t *)link->priv)->stop = 1;
+       nsp_cs_release(link);
 
        kfree(link->priv);
        link->priv = NULL;
@@ -1672,9 +1663,9 @@ static void nsp_cs_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 /*====================================================================*/
-static void nsp_cs_config(dev_link_t *link)
+static int nsp_cs_config(struct pcmcia_device *link)
 {
-       client_handle_t   handle = link->handle;
+       int               ret;
        scsi_info_t      *info   = link->priv;
        tuple_t           tuple;
        cisparse_t        parse;
@@ -1698,26 +1689,22 @@ static void nsp_cs_config(dev_link_t *link)
        tuple.TupleData       = tuple_data;
        tuple.TupleDataMax    = sizeof(tuple_data);
        tuple.TupleOffset     = 0;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData,  pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple,    pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData,  pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple,    pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present    = parse.config.rmask[0];
 
-       /* Configure card */
-       link->state           |= DEV_CONFIG;
-
        /* Look up the current Vcc */
-       CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
-       link->conf.Vcc = conf.Vcc;
+       CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &conf));
 
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        while (1) {
                cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
 
-               if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-                               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+               if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+                               pcmcia_parse_tuple(link, &tuple, &parse) != 0)
                        goto next_entry;
 
                if (cfg->flags & CISTPL_CFTABLE_DEFAULT) { dflt = *cfg; }
@@ -1743,10 +1730,10 @@ static void nsp_cs_config(dev_link_t *link)
                }
 
                if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) {
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
                } else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) {
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
                }
 
@@ -1773,7 +1760,7 @@ static void nsp_cs_config(dev_link_t *link)
                                link->io.NumPorts2 = io->win[1].len;
                        }
                        /* This reserves IO space but doesn't actually enable it */
-                       if (pcmcia_request_io(link->handle, &link->io) != 0)
+                       if (pcmcia_request_io(link, &link->io) != 0)
                                goto next_entry;
                }
 
@@ -1788,7 +1775,7 @@ static void nsp_cs_config(dev_link_t *link)
                                req.Size = 0x1000;
                        }
                        req.AccessSpeed = 0;
-                       if (pcmcia_request_window(&link->handle, &req, &link->win) != 0)
+                       if (pcmcia_request_window(&link, &req, &link->win) != 0)
                                goto next_entry;
                        map.Page = 0; map.CardOffset = mem->win[0].card_addr;
                        if (pcmcia_map_mem_page(link->win, &map) != 0)
@@ -1802,17 +1789,14 @@ static void nsp_cs_config(dev_link_t *link)
 
        next_entry:
                nsp_dbg(NSP_DEBUG_INIT, "next");
-
-               if (link->io.NumPorts1) {
-                       pcmcia_release_io(link->handle, &link->io);
-               }
-               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+               pcmcia_disable_device(link);
+               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
        }
 
        if (link->conf.Attributes & CONF_ENABLE_IRQ) {
-               CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+               CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
        }
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
        if (free_ports) {
                if (link->io.BasePort1) {
@@ -1854,16 +1838,19 @@ static void nsp_cs_config(dev_link_t *link)
 
 
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,74))
-       scsi_add_host (host, NULL);
+       ret = scsi_add_host (host, NULL);
+       if (ret)
+               goto cs_failed;
+
        scsi_scan_host(host);
 
        snprintf(info->node.dev_name, sizeof(info->node.dev_name), "scsi%d", host->host_no);
-       link->dev  = &info->node;
+       link->dev_node  = &info->node;
        info->host = host;
 
 #else
        nsp_dbg(NSP_DEBUG_INIT, "GET_SCSI_INFO");
-       tail = &link->dev;
+       tail = &link->dev_node;
        info->ndev = 0;
 
        nsp_dbg(NSP_DEBUG_INIT, "host=0x%p", host);
@@ -1908,11 +1895,10 @@ static void nsp_cs_config(dev_link_t *link)
 #endif
 
        /* Finally, report what we've done */
-       printk(KERN_INFO "nsp_cs: index 0x%02x: Vcc %d.%d",
-              link->conf.ConfigIndex,
-              link->conf.Vcc/10, link->conf.Vcc%10);
-       if (link->conf.Vpp1) {
-               printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+       printk(KERN_INFO "nsp_cs: index 0x%02x: ",
+              link->conf.ConfigIndex);
+       if (link->conf.Vpp) {
+               printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10);
        }
        if (link->conf.Attributes & CONF_ENABLE_IRQ) {
                printk(", irq %d", link->irq.AssignedIRQ);
@@ -1929,15 +1915,14 @@ static void nsp_cs_config(dev_link_t *link)
                       req.Base+req.Size-1);
        printk("\n");
 
-       link->state &= ~DEV_CONFIG_PENDING;
-       return;
+       return 0;
 
  cs_failed:
        nsp_dbg(NSP_DEBUG_INIT, "config fail");
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
        nsp_cs_release(link);
 
-       return;
+       return -ENODEV;
 } /* nsp_cs_config */
 #undef CS_CHECK
 
@@ -1947,7 +1932,7 @@ static void nsp_cs_config(dev_link_t *link)
     device, and release the PCMCIA configuration.  If the device is
     still open, this will be postponed until it is closed.
 ======================================================================*/
-static void nsp_cs_release(dev_link_t *link)
+static void nsp_cs_release(struct pcmcia_device *link)
 {
        scsi_info_t *info = link->priv;
        nsp_hw_data *data = NULL;
@@ -1968,22 +1953,15 @@ static void nsp_cs_release(dev_link_t *link)
 #else
        scsi_unregister_host(&nsp_driver_template);
 #endif
-       link->dev = NULL;
+       link->dev_node = NULL;
 
        if (link->win) {
                if (data != NULL) {
                        iounmap((void *)(data->MmioAddress));
                }
-               pcmcia_release_window(link->win);
-       }
-       pcmcia_release_configuration(link->handle);
-       if (link->io.NumPorts1) {
-               pcmcia_release_io(link->handle, &link->io);
        }
-       if (link->irq.AssignedIRQ) {
-               pcmcia_release_irq(link->handle, &link->irq);
-       }
-       link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
+
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,2))
        if (info->host != NULL) {
                scsi_host_put(info->host);
@@ -1991,14 +1969,11 @@ static void nsp_cs_release(dev_link_t *link)
 #endif
 } /* nsp_cs_release */
 
-static int nsp_cs_suspend(struct pcmcia_device *dev)
+static int nsp_cs_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        scsi_info_t *info = link->priv;
        nsp_hw_data *data;
 
-       link->state |= DEV_SUSPEND;
-
        nsp_dbg(NSP_DEBUG_INIT, "event: suspend");
 
        if (info->host != NULL) {
@@ -2011,25 +1986,16 @@ static int nsp_cs_suspend(struct pcmcia_device *dev)
 
        info->stop = 1;
 
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
        return 0;
 }
 
-static int nsp_cs_resume(struct pcmcia_device *dev)
+static int nsp_cs_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        scsi_info_t *info = link->priv;
        nsp_hw_data *data;
 
        nsp_dbg(NSP_DEBUG_INIT, "event: resume");
 
-       link->state &= ~DEV_SUSPEND;
-
-       if (link->state & DEV_CONFIG)
-               pcmcia_request_configuration(link->handle, &link->conf);
-
        info->stop = 0;
 
        if (info->host != NULL) {
@@ -2065,7 +2031,7 @@ static struct pcmcia_driver nsp_driver = {
        .drv            = {
                .name   = "nsp_cs",
        },
-       .probe          = nsp_cs_attach,
+       .probe          = nsp_cs_probe,
        .remove         = nsp_cs_detach,
        .id_table       = nsp_cs_ids,
        .suspend        = nsp_cs_suspend,
@@ -2098,19 +2064,7 @@ static int __init nsp_cs_init(void)
 static void __exit nsp_cs_exit(void)
 {
        nsp_msg(KERN_INFO, "unloading...");
-
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,68))
        pcmcia_unregister_driver(&nsp_driver);
-#else
-       unregister_pcmcia_driver(&dev_info);
-       /* XXX: this really needs to move into generic code.. */
-       while (dev_list != NULL) {
-               if (dev_list->state & DEV_CONFIG) {
-                       nsp_cs_release(dev_list);
-               }
-               nsp_cs_detach(dev_list);
-       }
-#endif
 }
 
 
index b66b140..8908b8e 100644 (file)
 /*====================================================================*/
 
 typedef struct scsi_info_t {
-       dev_link_t             link;
+       struct pcmcia_device    *p_dev;
        struct Scsi_Host      *host;
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,74))
        dev_node_t             node;
@@ -297,8 +297,8 @@ typedef struct _nsp_hw_data {
 
 /* Card service functions */
 static void        nsp_cs_detach (struct pcmcia_device *p_dev);
-static void        nsp_cs_release(dev_link_t *link);
-static void        nsp_cs_config (dev_link_t *link);
+static void        nsp_cs_release(struct pcmcia_device *link);
+static int        nsp_cs_config (struct pcmcia_device *link);
 
 /* Linux SCSI subsystem specific functions */
 static struct Scsi_Host *nsp_detect     (struct scsi_host_template *sht);
@@ -450,7 +450,7 @@ static inline struct Scsi_Host *scsi_host_hn_get(unsigned short hostno)
        return host;
 }
 
-static void cs_error(client_handle_t handle, int func, int ret)
+static void cs_error(struct pcmcia_device *handle, int func, int ret)
 {
        error_info_t err = { func, ret };
        pcmcia_report_error(handle, &err);
index dce7e68..86c2ac6 100644 (file)
@@ -91,18 +91,18 @@ static struct scsi_host_template qlogicfas_driver_template = {
 /*====================================================================*/
 
 typedef struct scsi_info_t {
-       dev_link_t link;
+       struct pcmcia_device    *p_dev;
        dev_node_t node;
        struct Scsi_Host *host;
        unsigned short manf_id;
 } scsi_info_t;
 
-static void qlogic_release(dev_link_t *link);
+static void qlogic_release(struct pcmcia_device *link);
 static void qlogic_detach(struct pcmcia_device *p_dev);
-static void qlogic_config(dev_link_t * link);
+static int qlogic_config(struct pcmcia_device * link);
 
 static struct Scsi_Host *qlogic_detect(struct scsi_host_template *host,
-                               dev_link_t *link, int qbase, int qlirq)
+                               struct pcmcia_device *link, int qbase, int qlirq)
 {
        int qltyp;              /* type of chip */
        int qinitid;
@@ -156,10 +156,9 @@ free_scsi_host:
 err:
        return NULL;
 }
-static int qlogic_attach(struct pcmcia_device *p_dev)
+static int qlogic_probe(struct pcmcia_device *link)
 {
        scsi_info_t *info;
-       dev_link_t *link;
 
        DEBUG(0, "qlogic_attach()\n");
 
@@ -168,7 +167,7 @@ static int qlogic_attach(struct pcmcia_device *p_dev)
        if (!info)
                return -ENOMEM;
        memset(info, 0, sizeof(*info));
-       link = &info->link;
+       info->p_dev = link;
        link->priv = info;
        link->io.NumPorts1 = 16;
        link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
@@ -176,30 +175,19 @@ static int qlogic_attach(struct pcmcia_device *p_dev)
        link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
        link->irq.IRQInfo1 = IRQ_LEVEL_ID;
        link->conf.Attributes = CONF_ENABLE_IRQ;
-       link->conf.Vcc = 50;
        link->conf.IntType = INT_MEMORY_AND_IO;
        link->conf.Present = PRESENT_OPTION;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       qlogic_config(link);
-
-       return 0;
+       return qlogic_config(link);
 }                              /* qlogic_attach */
 
 /*====================================================================*/
 
-static void qlogic_detach(struct pcmcia_device *p_dev)
+static void qlogic_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
-
        DEBUG(0, "qlogic_detach(0x%p)\n", link);
 
-       if (link->state & DEV_CONFIG)
-               qlogic_release(link);
-
+       qlogic_release(link);
        kfree(link->priv);
 
 }                              /* qlogic_detach */
@@ -209,9 +197,8 @@ static void qlogic_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void qlogic_config(dev_link_t * link)
+static int qlogic_config(struct pcmcia_device * link)
 {
-       client_handle_t handle = link->handle;
        scsi_info_t *info = link->priv;
        tuple_t tuple;
        cisparse_t parse;
@@ -225,38 +212,35 @@ static void qlogic_config(dev_link_t * link)
        tuple.TupleDataMax = 64;
        tuple.TupleOffset = 0;
        tuple.DesiredTuple = CISTPL_CONFIG;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
 
        tuple.DesiredTuple = CISTPL_MANFID;
-       if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
+       if ((pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(link, &tuple) == CS_SUCCESS))
                info->manf_id = le16_to_cpu(tuple.TupleData[0]);
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        while (1) {
-               if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-                               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+               if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+                               pcmcia_parse_tuple(link, &tuple, &parse) != 0)
                        goto next_entry;
                link->conf.ConfigIndex = parse.cftable_entry.index;
                link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
                link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
                if (link->io.BasePort1 != 0) {
-                       i = pcmcia_request_io(handle, &link->io);
+                       i = pcmcia_request_io(link, &link->io);
                        if (i == CS_SUCCESS)
                                break;
                }
              next_entry:
-               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
        }
 
-       CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+       CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
        if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
                /* set ATAcmd */
@@ -275,82 +259,54 @@ static void qlogic_config(dev_link_t * link)
        
        if (!host) {
                printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
-               goto out;
+               goto cs_failed;
        }
 
        sprintf(info->node.dev_name, "scsi%d", host->host_no);
-       link->dev = &info->node;
+       link->dev_node = &info->node;
        info->host = host;
 
-out:
-       link->state &= ~DEV_CONFIG_PENDING;
-       return;
+       return 0;
 
 cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
-       link->dev = NULL;
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
-       link->state &= ~DEV_CONFIG;
-       return;
+       cs_error(link, last_fn, last_ret);
+       pcmcia_disable_device(link);
+       return -ENODEV;
 
 }                              /* qlogic_config */
 
 /*====================================================================*/
 
-static void qlogic_release(dev_link_t *link)
+static void qlogic_release(struct pcmcia_device *link)
 {
        scsi_info_t *info = link->priv;
 
        DEBUG(0, "qlogic_release(0x%p)\n", link);
 
        scsi_remove_host(info->host);
-       link->dev = NULL;
 
        free_irq(link->irq.AssignedIRQ, info->host);
-
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
+       pcmcia_disable_device(link);
 
        scsi_host_put(info->host);
-
-       link->state &= ~DEV_CONFIG;
 }
 
 /*====================================================================*/
 
-static int qlogic_suspend(struct pcmcia_device *dev)
+static int qlogic_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
+       scsi_info_t *info = link->priv;
 
-static int qlogic_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               scsi_info_t *info = link->priv;
-
-               pcmcia_request_configuration(link->handle, &link->conf);
-               if ((info->manf_id == MANFID_MACNICA) ||
-                   (info->manf_id == MANFID_PIONEER) ||
-                   (info->manf_id == 0x0098)) {
-                       outb(0x80, link->io.BasePort1 + 0xd);
-                       outb(0x24, link->io.BasePort1 + 0x9);
-                       outb(0x04, link->io.BasePort1 + 0xd);
-               }
-               /* Ugggglllyyyy!!! */
-               qlogicfas408_bus_reset(NULL);
+       pcmcia_request_configuration(link, &link->conf);
+       if ((info->manf_id == MANFID_MACNICA) ||
+           (info->manf_id == MANFID_PIONEER) ||
+           (info->manf_id == 0x0098)) {
+               outb(0x80, link->io.BasePort1 + 0xd);
+               outb(0x24, link->io.BasePort1 + 0x9);
+               outb(0x04, link->io.BasePort1 + 0xd);
        }
+       /* Ugggglllyyyy!!! */
+       qlogicfas408_bus_reset(NULL);
 
        return 0;
 }
@@ -382,10 +338,9 @@ static struct pcmcia_driver qlogic_cs_driver = {
        .drv            = {
        .name           = "qlogic_cs",
        },
-       .probe          = qlogic_attach,
+       .probe          = qlogic_probe,
        .remove         = qlogic_detach,
        .id_table       = qlogic_ids,
-       .suspend        = qlogic_suspend,
        .resume         = qlogic_resume,
 };
 
index 3a4dd6f..9f59827 100644 (file)
@@ -202,7 +202,7 @@ static char *version =
 /* ================================================================== */
 
 struct scsi_info_t {
-       dev_link_t link;
+       struct pcmcia_device    *p_dev;
        dev_node_t node;
        struct Scsi_Host *host;
        unsigned short manf_id;
@@ -527,7 +527,7 @@ idle_out:
 }
 
 static void
-SYM53C500_release(dev_link_t *link)
+SYM53C500_release(struct pcmcia_device *link)
 {
        struct scsi_info_t *info = link->priv;
        struct Scsi_Host *shost = info->host;
@@ -550,13 +550,7 @@ SYM53C500_release(dev_link_t *link)
        if (shost->io_port && shost->n_io_port)
                release_region(shost->io_port, shost->n_io_port);
 
-       link->dev = NULL;
-
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
-
-       link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
 
        scsi_host_put(shost);
 } /* SYM53C500_release */
@@ -713,10 +707,9 @@ static struct scsi_host_template sym53c500_driver_template = {
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void
-SYM53C500_config(dev_link_t *link)
+static int
+SYM53C500_config(struct pcmcia_device *link)
 {
-       client_handle_t handle = link->handle;
        struct scsi_info_t *info = link->priv;
        tuple_t tuple;
        cisparse_t parse;
@@ -733,40 +726,37 @@ SYM53C500_config(dev_link_t *link)
        tuple.TupleDataMax = 64;
        tuple.TupleOffset = 0;
        tuple.DesiredTuple = CISTPL_CONFIG;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
 
        tuple.DesiredTuple = CISTPL_MANFID;
-       if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
-           (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
+       if ((pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) &&
+           (pcmcia_get_tuple_data(link, &tuple) == CS_SUCCESS))
                info->manf_id = le16_to_cpu(tuple.TupleData[0]);
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        while (1) {
-               if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-                   pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+               if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+                   pcmcia_parse_tuple(link, &tuple, &parse) != 0)
                        goto next_entry;
                link->conf.ConfigIndex = parse.cftable_entry.index;
                link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
                link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
 
                if (link->io.BasePort1 != 0) {
-                       i = pcmcia_request_io(handle, &link->io);
+                       i = pcmcia_request_io(link, &link->io);
                        if (i == CS_SUCCESS)
                                break;
                }
 next_entry:
-               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
        }
 
-       CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+       CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
        /*
        *  That's the trouble with copying liberally from another driver.
@@ -835,7 +825,7 @@ next_entry:
        data->fast_pio = USE_FAST_PIO;
 
        sprintf(info->node.dev_name, "scsi%d", host->host_no);
-       link->dev = &info->node;
+       link->dev_node = &info->node;
        info->host = host;
 
        if (scsi_add_host(host, NULL))
@@ -843,7 +833,7 @@ next_entry:
 
        scsi_scan_host(host);
 
-       goto out;       /* SUCCESS */
+       return 0;
 
 err_free_irq:
        free_irq(irq_level, host);
@@ -852,74 +842,50 @@ err_free_scsi:
 err_release:
        release_region(port_base, 0x10);
        printk(KERN_INFO "sym53c500_cs: no SCSI devices found\n");
-
-out:
-       link->state &= ~DEV_CONFIG_PENDING;
-       return;
+       return -ENODEV;
 
 cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
        SYM53C500_release(link);
-       return;
+       return -ENODEV;
 } /* SYM53C500_config */
 
-static int sym53c500_suspend(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int sym53c500_resume(struct pcmcia_device *dev)
+static int sym53c500_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        struct scsi_info_t *info = link->priv;
 
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG) {
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-               /* See earlier comment about manufacturer IDs. */
-               if ((info->manf_id == MANFID_MACNICA) ||
-                   (info->manf_id == MANFID_PIONEER) ||
-                   (info->manf_id == 0x0098)) {
-                       outb(0x80, link->io.BasePort1 + 0xd);
-                       outb(0x24, link->io.BasePort1 + 0x9);
-                       outb(0x04, link->io.BasePort1 + 0xd);
-               }
-               /*
-                *  If things don't work after a "resume",
-                *  this is a good place to start looking.
-                */
-               SYM53C500_int_host_reset(link->io.BasePort1);
+       /* See earlier comment about manufacturer IDs. */
+       if ((info->manf_id == MANFID_MACNICA) ||
+           (info->manf_id == MANFID_PIONEER) ||
+           (info->manf_id == 0x0098)) {
+               outb(0x80, link->io.BasePort1 + 0xd);
+               outb(0x24, link->io.BasePort1 + 0x9);
+               outb(0x04, link->io.BasePort1 + 0xd);
        }
+       /*
+        *  If things don't work after a "resume",
+        *  this is a good place to start looking.
+        */
+       SYM53C500_int_host_reset(link->io.BasePort1);
 
        return 0;
 }
 
 static void
-SYM53C500_detach(struct pcmcia_device *p_dev)
+SYM53C500_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
-
        DEBUG(0, "SYM53C500_detach(0x%p)\n", link);
 
-       if (link->state & DEV_CONFIG)
-               SYM53C500_release(link);
+       SYM53C500_release(link);
 
        kfree(link->priv);
        link->priv = NULL;
 } /* SYM53C500_detach */
 
 static int
-SYM53C500_attach(struct pcmcia_device *p_dev)
+SYM53C500_probe(struct pcmcia_device *link)
 {
        struct scsi_info_t *info;
-       dev_link_t *link;
 
        DEBUG(0, "SYM53C500_attach()\n");
 
@@ -928,7 +894,7 @@ SYM53C500_attach(struct pcmcia_device *p_dev)
        if (!info)
                return -ENOMEM;
        memset(info, 0, sizeof(*info));
-       link = &info->link;
+       info->p_dev = link;
        link->priv = info;
        link->io.NumPorts1 = 16;
        link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
@@ -936,17 +902,10 @@ SYM53C500_attach(struct pcmcia_device *p_dev)
        link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
        link->irq.IRQInfo1 = IRQ_LEVEL_ID;
        link->conf.Attributes = CONF_ENABLE_IRQ;
-       link->conf.Vcc = 50;
        link->conf.IntType = INT_MEMORY_AND_IO;
        link->conf.Present = PRESENT_OPTION;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       SYM53C500_config(link);
-
-       return 0;
+       return SYM53C500_config(link);
 } /* SYM53C500_attach */
 
 MODULE_AUTHOR("Bob Tracy <rct@frus.com>");
@@ -966,10 +925,9 @@ static struct pcmcia_driver sym53c500_cs_driver = {
        .drv            = {
                .name   = "sym53c500_cs",
        },
-       .probe          = SYM53C500_attach,
+       .probe          = SYM53C500_probe,
        .remove         = SYM53C500_detach,
        .id_table       = sym53c500_ids,
-       .suspend        = sym53c500_suspend,
        .resume         = sym53c500_resume,
 };
 
index dfc1e86..043f50b 100644 (file)
@@ -20,7 +20,7 @@
  *
  * Contact Information:
  * Scott H Kilau <Scott_Kilau@digi.com>
- * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ * Wendy Xiong   <wendyx@us.ibm.com>
  *
  ***********************************************************************/
 
index b1b66e7..b3e1f71 100644 (file)
@@ -20,7 +20,7 @@
  *
  * Contact Information:
  * Scott H Kilau <Scott_Kilau@digi.com>
- * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ * Wendy Xiong   <wendyx@us.ibm.com>
  *
  *
  ***********************************************************************/
index 87e4e2c..a5fc589 100644 (file)
@@ -20,7 +20,7 @@
  *
  * Contact Information:
  * Scott H Kilau <Scott_Kilau@digi.com>
- * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ * Wendy Xiong   <wendyx@us.ibm.com>
  *
  ***********************************************************************/
 #include <linux/delay.h>       /* For udelay */
index c303336..2c70773 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/serial_core.h>
+#include <linux/delay.h>
 #include <linux/major.h>
 #include <asm/io.h>
 #include <asm/system.h>
@@ -97,11 +98,13 @@ static const struct multi_id multi_id[] = {
 #define MULTI_COUNT (sizeof(multi_id)/sizeof(struct multi_id))
 
 struct serial_info {
-       dev_link_t              link;
+       struct pcmcia_device    *p_dev;
        int                     ndev;
        int                     multi;
        int                     slave;
        int                     manfid;
+       int                     prodid;
+       int                     c950ctrl;
        dev_node_t              node[4];
        int                     line[4];
 };
@@ -113,9 +116,36 @@ struct serial_cfg_mem {
 };
 
 
-static void serial_config(dev_link_t * link);
+static int serial_config(struct pcmcia_device * link);
 
 
+static void wakeup_card(struct serial_info *info)
+{
+       int ctrl = info->c950ctrl;
+
+       if (info->manfid == MANFID_OXSEMI) {
+               outb(12, ctrl + 1);
+       } else if (info->manfid == MANFID_POSSIO && info->prodid == PRODID_POSSIO_GCC) {
+               /* request_region? oxsemi branch does no request_region too... */
+               /* This sequence is needed to properly initialize MC45 attached to OXCF950.
+                * I tried decreasing these msleep()s, but it worked properly (survived
+                * 1000 stop/start operations) with these timeouts (or bigger). */
+               outb(0xA, ctrl + 1);
+               msleep(100);
+               outb(0xE, ctrl + 1);
+               msleep(300);
+               outb(0xC, ctrl + 1);
+               msleep(100);
+               outb(0xE, ctrl + 1);
+               msleep(200);
+               outb(0xF, ctrl + 1);
+               msleep(100);
+               outb(0xE, ctrl + 1);
+               msleep(100);
+               outb(0xC, ctrl + 1);
+       }
+}
+
 /*======================================================================
 
     After a card is removed, serial_remove() will unregister
@@ -123,67 +153,45 @@ static void serial_config(dev_link_t * link);
     
 ======================================================================*/
 
-static void serial_remove(dev_link_t *link)
+static void serial_remove(struct pcmcia_device *link)
 {
        struct serial_info *info = link->priv;
        int i;
 
-       link->state &= ~DEV_PRESENT;
-
        DEBUG(0, "serial_release(0x%p)\n", link);
 
        /*
         * Recheck to see if the device is still configured.
         */
-       if (info->link.state & DEV_CONFIG) {
-               for (i = 0; i < info->ndev; i++)
-                       serial8250_unregister_port(info->line[i]);
+       for (i = 0; i < info->ndev; i++)
+               serial8250_unregister_port(info->line[i]);
 
-               info->link.dev = NULL;
+       info->p_dev->dev_node = NULL;
 
-               if (!info->slave) {
-                       pcmcia_release_configuration(info->link.handle);
-                       pcmcia_release_io(info->link.handle, &info->link.io);
-                       pcmcia_release_irq(info->link.handle, &info->link.irq);
-               }
-
-               info->link.state &= ~DEV_CONFIG;
-       }
+       if (!info->slave)
+               pcmcia_disable_device(link);
 }
 
-static int serial_suspend(struct pcmcia_device *dev)
+static int serial_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
-       link->state |= DEV_SUSPEND;
-
-       if (link->state & DEV_CONFIG) {
-               struct serial_info *info = link->priv;
-               int i;
-
-               for (i = 0; i < info->ndev; i++)
-                       serial8250_suspend_port(info->line[i]);
+       struct serial_info *info = link->priv;
+       int i;
 
-               if (!info->slave)
-                       pcmcia_release_configuration(link->handle);
-       }
+       for (i = 0; i < info->ndev; i++)
+               serial8250_suspend_port(info->line[i]);
 
        return 0;
 }
 
-static int serial_resume(struct pcmcia_device *dev)
+static int serial_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
-       link->state &= ~DEV_SUSPEND;
-
-       if (DEV_OK(link)) {
+       if (pcmcia_dev_present(link)) {
                struct serial_info *info = link->priv;
                int i;
 
-               if (!info->slave)
-                       pcmcia_request_configuration(link->handle, &link->conf);
-
                for (i = 0; i < info->ndev; i++)
                        serial8250_resume_port(info->line[i]);
+               wakeup_card(info);
        }
 
        return 0;
@@ -197,10 +205,9 @@ static int serial_resume(struct pcmcia_device *dev)
 
 ======================================================================*/
 
-static int serial_probe(struct pcmcia_device *p_dev)
+static int serial_probe(struct pcmcia_device *link)
 {
        struct serial_info *info;
-       dev_link_t *link;
 
        DEBUG(0, "serial_attach()\n");
 
@@ -209,7 +216,7 @@ static int serial_probe(struct pcmcia_device *p_dev)
        if (!info)
                return -ENOMEM;
        memset(info, 0, sizeof (*info));
-       link = &info->link;
+       info->p_dev = link;
        link->priv = info;
 
        link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
@@ -223,12 +230,7 @@ static int serial_probe(struct pcmcia_device *p_dev)
        }
        link->conf.IntType = INT_MEMORY_AND_IO;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       serial_config(link);
-
-       return 0;
+       return serial_config(link);
 }
 
 /*======================================================================
@@ -240,9 +242,8 @@ static int serial_probe(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void serial_detach(struct pcmcia_device *p_dev)
+static void serial_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct serial_info *info = link->priv;
 
        DEBUG(0, "serial_detach(0x%p)\n", link);
@@ -263,7 +264,7 @@ static void serial_detach(struct pcmcia_device *p_dev)
 
 /*====================================================================*/
 
-static int setup_serial(client_handle_t handle, struct serial_info * info,
+static int setup_serial(struct pcmcia_device *handle, struct serial_info * info,
                        kio_addr_t iobase, int irq)
 {
        struct uart_port port;
@@ -298,7 +299,7 @@ static int setup_serial(client_handle_t handle, struct serial_info * info,
 /*====================================================================*/
 
 static int
-first_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse)
+first_tuple(struct pcmcia_device *handle, tuple_t * tuple, cisparse_t * parse)
 {
        int i;
        i = pcmcia_get_first_tuple(handle, tuple);
@@ -311,7 +312,7 @@ first_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse)
 }
 
 static int
-next_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse)
+next_tuple(struct pcmcia_device *handle, tuple_t * tuple, cisparse_t * parse)
 {
        int i;
        i = pcmcia_get_next_tuple(handle, tuple);
@@ -325,11 +326,10 @@ next_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse)
 
 /*====================================================================*/
 
-static int simple_config(dev_link_t *link)
+static int simple_config(struct pcmcia_device *link)
 {
        static const kio_addr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
        static const int size_table[2] = { 8, 16 };
-       client_handle_t handle = link->handle;
        struct serial_info *info = link->priv;
        struct serial_cfg_mem *cfg_mem;
        tuple_t *tuple;
@@ -350,7 +350,7 @@ static int simple_config(dev_link_t *link)
        buf = cfg_mem->buf;
 
        /* If the card is already configured, look up the port and irq */
-       i = pcmcia_get_configuration_info(handle, &config);
+       i = pcmcia_get_configuration_info(link, &config);
        if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) {
                kio_addr_t port = 0;
                if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) {
@@ -363,10 +363,9 @@ static int simple_config(dev_link_t *link)
                }
                if (info->slave) {
                        kfree(cfg_mem);
-                       return setup_serial(handle, info, port, config.AssignedIRQ);
+                       return setup_serial(link, info, port, config.AssignedIRQ);
                }
        }
-       link->conf.Vcc = config.Vcc;
 
        /* First pass: look for a config entry that looks normal. */
        tuple->TupleData = (cisdata_t *) buf;
@@ -377,12 +376,12 @@ static int simple_config(dev_link_t *link)
        /* Two tries: without IO aliases, then with aliases */
        for (s = 0; s < 2; s++) {
                for (try = 0; try < 2; try++) {
-                       i = first_tuple(handle, tuple, parse);
+                       i = first_tuple(link, tuple, parse);
                        while (i != CS_NO_MORE_ITEMS) {
                                if (i != CS_SUCCESS)
                                        goto next_entry;
                                if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
-                                       link->conf.Vpp1 = link->conf.Vpp2 =
+                                       link->conf.Vpp =
                                            cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
                                if ((cf->io.nwin > 0) && (cf->io.win[0].len == size_table[s]) &&
                                            (cf->io.win[0].base != 0)) {
@@ -390,19 +389,19 @@ static int simple_config(dev_link_t *link)
                                        link->io.BasePort1 = cf->io.win[0].base;
                                        link->io.IOAddrLines = (try == 0) ?
                                            16 : cf->io.flags & CISTPL_IO_LINES_MASK;
-                                       i = pcmcia_request_io(link->handle, &link->io);
+                                       i = pcmcia_request_io(link, &link->io);
                                        if (i == CS_SUCCESS)
                                                goto found_port;
                                }
 next_entry:
-                               i = next_tuple(handle, tuple, parse);
+                               i = next_tuple(link, tuple, parse);
                        }
                }
        }
        /* Second pass: try to find an entry that isn't picky about
           its base address, then try to grab any standard serial port
           address, and finally try to get any free port. */
-       i = first_tuple(handle, tuple, parse);
+       i = first_tuple(link, tuple, parse);
        while (i != CS_NO_MORE_ITEMS) {
                if ((i == CS_SUCCESS) && (cf->io.nwin > 0) &&
                    ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
@@ -410,50 +409,48 @@ next_entry:
                        for (j = 0; j < 5; j++) {
                                link->io.BasePort1 = base[j];
                                link->io.IOAddrLines = base[j] ? 16 : 3;
-                               i = pcmcia_request_io(link->handle, &link->io);
+                               i = pcmcia_request_io(link, &link->io);
                                if (i == CS_SUCCESS)
                                        goto found_port;
                        }
                }
-               i = next_tuple(handle, tuple, parse);
+               i = next_tuple(link, tuple, parse);
        }
 
       found_port:
        if (i != CS_SUCCESS) {
                printk(KERN_NOTICE
                       "serial_cs: no usable port range found, giving up\n");
-               cs_error(link->handle, RequestIO, i);
+               cs_error(link, RequestIO, i);
                kfree(cfg_mem);
                return -1;
        }
 
-       i = pcmcia_request_irq(link->handle, &link->irq);
+       i = pcmcia_request_irq(link, &link->irq);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestIRQ, i);
+               cs_error(link, RequestIRQ, i);
                link->irq.AssignedIRQ = 0;
        }
        if (info->multi && (info->manfid == MANFID_3COM))
                link->conf.ConfigIndex &= ~(0x08);
-       i = pcmcia_request_configuration(link->handle, &link->conf);
+       i = pcmcia_request_configuration(link, &link->conf);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestConfiguration, i);
+               cs_error(link, RequestConfiguration, i);
                kfree(cfg_mem);
                return -1;
        }
        kfree(cfg_mem);
-       return setup_serial(handle, info, link->io.BasePort1, link->irq.AssignedIRQ);
+       return setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ);
 }
 
-static int multi_config(dev_link_t * link)
+static int multi_config(struct pcmcia_device * link)
 {
-       client_handle_t handle = link->handle;
        struct serial_info *info = link->priv;
        struct serial_cfg_mem *cfg_mem;
        tuple_t *tuple;
        u_char *buf;
        cisparse_t *parse;
        cistpl_cftable_entry_t *cf;
-       config_info_t config;
        int i, rc, base2 = 0;
 
        cfg_mem = kmalloc(sizeof(struct serial_cfg_mem), GFP_KERNEL);
@@ -464,14 +461,6 @@ static int multi_config(dev_link_t * link)
        cf = &parse->cftable_entry;
        buf = cfg_mem->buf;
 
-       i = pcmcia_get_configuration_info(handle, &config);
-       if (i != CS_SUCCESS) {
-               cs_error(handle, GetConfigurationInfo, i);
-               rc = -1;
-               goto free_cfg_mem;
-       }
-       link->conf.Vcc = config.Vcc;
-
        tuple->TupleData = (cisdata_t *) buf;
        tuple->TupleOffset = 0;
        tuple->TupleDataMax = 255;
@@ -480,7 +469,7 @@ static int multi_config(dev_link_t * link)
 
        /* First, look for a generic full-sized window */
        link->io.NumPorts1 = info->multi * 8;
-       i = first_tuple(handle, tuple, parse);
+       i = first_tuple(link, tuple, parse);
        while (i != CS_NO_MORE_ITEMS) {
                /* The quad port cards have bad CIS's, so just look for a
                   window larger than 8 ports and assume it will be right */
@@ -490,19 +479,19 @@ static int multi_config(dev_link_t * link)
                        link->io.BasePort1 = cf->io.win[0].base;
                        link->io.IOAddrLines =
                            cf->io.flags & CISTPL_IO_LINES_MASK;
-                       i = pcmcia_request_io(link->handle, &link->io);
+                       i = pcmcia_request_io(link, &link->io);
                        base2 = link->io.BasePort1 + 8;
                        if (i == CS_SUCCESS)
                                break;
                }
-               i = next_tuple(handle, tuple, parse);
+               i = next_tuple(link, tuple, parse);
        }
 
        /* If that didn't work, look for two windows */
        if (i != CS_SUCCESS) {
                link->io.NumPorts1 = link->io.NumPorts2 = 8;
                info->multi = 2;
-               i = first_tuple(handle, tuple, parse);
+               i = first_tuple(link, tuple, parse);
                while (i != CS_NO_MORE_ITEMS) {
                        if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) {
                                link->conf.ConfigIndex = cf->index;
@@ -510,26 +499,26 @@ static int multi_config(dev_link_t * link)
                                link->io.BasePort2 = cf->io.win[1].base;
                                link->io.IOAddrLines =
                                    cf->io.flags & CISTPL_IO_LINES_MASK;
-                               i = pcmcia_request_io(link->handle, &link->io);
+                               i = pcmcia_request_io(link, &link->io);
                                base2 = link->io.BasePort2;
                                if (i == CS_SUCCESS)
                                        break;
                        }
-                       i = next_tuple(handle, tuple, parse);
+                       i = next_tuple(link, tuple, parse);
                }
        }
 
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestIO, i);
+               cs_error(link, RequestIO, i);
                rc = -1;
                goto free_cfg_mem;
        }
 
-       i = pcmcia_request_irq(link->handle, &link->irq);
+       i = pcmcia_request_irq(link, &link->irq);
        if (i != CS_SUCCESS) {
                printk(KERN_NOTICE
                       "serial_cs: no usable port range found, giving up\n");
-               cs_error(link->handle, RequestIRQ, i);
+               cs_error(link, RequestIRQ, i);
                link->irq.AssignedIRQ = 0;
        }
        /* Socket Dual IO: this enables irq's for second port */
@@ -537,35 +526,43 @@ static int multi_config(dev_link_t * link)
                link->conf.Present |= PRESENT_EXT_STATUS;
                link->conf.ExtStatus = ESR_REQ_ATTN_ENA;
        }
-       i = pcmcia_request_configuration(link->handle, &link->conf);
+       i = pcmcia_request_configuration(link, &link->conf);
        if (i != CS_SUCCESS) {
-               cs_error(link->handle, RequestConfiguration, i);
+               cs_error(link, RequestConfiguration, i);
                rc = -1;
                goto free_cfg_mem;
        }
 
        /* The Oxford Semiconductor OXCF950 cards are in fact single-port:
-          8 registers are for the UART, the others are extra registers */
-       if (info->manfid == MANFID_OXSEMI) {
+        * 8 registers are for the UART, the others are extra registers.
+        * Siemen's MC45 PCMCIA (Possio's GCC) is OXCF950 based too.
+        */
+       if (info->manfid == MANFID_OXSEMI || (info->manfid == MANFID_POSSIO &&
+                               info->prodid == PRODID_POSSIO_GCC)) {
+               int err;
+
                if (cf->index == 1 || cf->index == 3) {
-                       setup_serial(handle, info, base2, link->irq.AssignedIRQ);
-                       outb(12, link->io.BasePort1 + 1);
+                       err = setup_serial(link, info, base2,
+                                       link->irq.AssignedIRQ);
+                       base2 = link->io.BasePort1;
                } else {
-                       setup_serial(handle, info, link->io.BasePort1, link->irq.AssignedIRQ);
-                       outb(12, base2 + 1);
+                       err = setup_serial(link, info, link->io.BasePort1,
+                                       link->irq.AssignedIRQ);
                }
+               info->c950ctrl = base2;
+               wakeup_card(info);
                rc = 0;
                goto free_cfg_mem;
        }
 
-       setup_serial(handle, info, link->io.BasePort1, link->irq.AssignedIRQ);
+       setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ);
        /* The Nokia cards are not really multiport cards */
        if (info->manfid == MANFID_NOKIA) {
                rc = 0;
                goto free_cfg_mem;
        }
        for (i = 0; i < info->multi - 1; i++)
-               setup_serial(handle, info, base2 + (8 * i),
+               setup_serial(link, info, base2 + (8 * i),
                                link->irq.AssignedIRQ);
        rc = 0;
 free_cfg_mem:
@@ -581,9 +578,8 @@ free_cfg_mem:
 
 ======================================================================*/
 
-void serial_config(dev_link_t * link)
+static int serial_config(struct pcmcia_device * link)
 {
-       client_handle_t handle = link->handle;
        struct serial_info *info = link->priv;
        struct serial_cfg_mem *cfg_mem;
        tuple_t *tuple;
@@ -609,7 +605,7 @@ void serial_config(dev_link_t * link)
        tuple->Attributes = 0;
        /* Get configuration register information */
        tuple->DesiredTuple = CISTPL_CONFIG;
-       last_ret = first_tuple(handle, tuple, parse);
+       last_ret = first_tuple(link, tuple, parse);
        if (last_ret != CS_SUCCESS) {
                last_fn = ParseTuple;
                goto cs_failed;
@@ -617,18 +613,16 @@ void serial_config(dev_link_t * link)
        link->conf.ConfigBase = parse->config.base;
        link->conf.Present = parse->config.rmask[0];
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
        /* Is this a compliant multifunction card? */
        tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
        tuple->Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
-       info->multi = (first_tuple(handle, tuple, parse) == CS_SUCCESS);
+       info->multi = (first_tuple(link, tuple, parse) == CS_SUCCESS);
 
        /* Is this a multiport card? */
        tuple->DesiredTuple = CISTPL_MANFID;
-       if (first_tuple(handle, tuple, parse) == CS_SUCCESS) {
+       if (first_tuple(link, tuple, parse) == CS_SUCCESS) {
                info->manfid = parse->manfid.manf;
+               info->prodid = le16_to_cpu(buf[1]);
                for (i = 0; i < MULTI_COUNT; i++)
                        if ((info->manfid == multi_id[i].manfid) &&
                            (parse->manfid.card == multi_id[i].prodid))
@@ -641,11 +635,11 @@ void serial_config(dev_link_t * link)
           multifunction cards that ask for appropriate IO port ranges */
        tuple->DesiredTuple = CISTPL_FUNCID;
        if ((info->multi == 0) &&
-           ((first_tuple(handle, tuple, parse) != CS_SUCCESS) ||
+           ((first_tuple(link, tuple, parse) != CS_SUCCESS) ||
             (parse->funcid.func == CISTPL_FUNCID_MULTI) ||
             (parse->funcid.func == CISTPL_FUNCID_SERIAL))) {
                tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
-               if (first_tuple(handle, tuple, parse) == CS_SUCCESS) {
+               if (first_tuple(link, tuple, parse) == CS_SUCCESS) {
                        if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0))
                                info->multi = cf->io.win[0].len >> 3;
                        if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) &&
@@ -664,31 +658,30 @@ void serial_config(dev_link_t * link)
 
        if (info->manfid == MANFID_IBM) {
                conf_reg_t reg = { 0, CS_READ, 0x800, 0 };
-               last_ret = pcmcia_access_configuration_register(link->handle, &reg);
+               last_ret = pcmcia_access_configuration_register(link, &reg);
                if (last_ret) {
                        last_fn = AccessConfigurationRegister;
                        goto cs_failed;
                }
                reg.Action = CS_WRITE;
                reg.Value = reg.Value | 1;
-               last_ret = pcmcia_access_configuration_register(link->handle, &reg);
+               last_ret = pcmcia_access_configuration_register(link, &reg);
                if (last_ret) {
                        last_fn = AccessConfigurationRegister;
                        goto cs_failed;
                }
        }
 
-       link->dev = &info->node[0];
-       link->state &= ~DEV_CONFIG_PENDING;
+       link->dev_node = &info->node[0];
        kfree(cfg_mem);
-       return;
+       return 0;
 
  cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
  failed:
        serial_remove(link);
-       link->state &= ~DEV_CONFIG_PENDING;
        kfree(cfg_mem);
+       return -ENODEV;
 }
 
 static struct pcmcia_device_id serial_ids[] = {
@@ -739,6 +732,7 @@ static struct pcmcia_device_id serial_ids[] = {
        PCMCIA_MFC_DEVICE_PROD_ID1(1, "Motorola MARQUIS", 0xf03e4e77),
        PCMCIA_MFC_DEVICE_PROD_ID2(1, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302),
        PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0301),
+       PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x0276),
        PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039),
        PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006),
        PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a),
@@ -757,6 +751,7 @@ static struct pcmcia_device_id serial_ids[] = {
        PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef),
        PCMCIA_DEVICE_PROD_ID124("TOSHIBA", "T144PF", "PCMCIA MODEM", 0xb4585a1a, 0x7271409c, 0xbd6c43ef),
        PCMCIA_DEVICE_PROD_ID123("FUJITSU", "FC14F ", "MBH10213", 0x6ee5a3d8, 0x30ead12b, 0xb00f05a0),
+       PCMCIA_DEVICE_PROD_ID123("Novatel Wireless", "Merlin UMTS Modem", "U630", 0x32607776, 0xd9e73b13, 0xe87332e),
        PCMCIA_DEVICE_PROD_ID13("MEGAHERTZ", "V.34 PCMCIA MODEM", 0xf510db04, 0xbb2cce4a),
        PCMCIA_DEVICE_PROD_ID12("Brain Boxes", "Bluetooth PC Card", 0xee138382, 0xd4ce9b02),
        PCMCIA_DEVICE_PROD_ID12("CIRRUS LOGIC", "FAX MODEM", 0xe625f451, 0xcecd6dfa),
index d3a7b0c..dda0ca4 100644 (file)
@@ -35,73 +35,52 @@ typedef struct ixj_info_t {
 } ixj_info_t;
 
 static void ixj_detach(struct pcmcia_device *p_dev);
-static void ixj_config(dev_link_t * link);
-static void ixj_cs_release(dev_link_t * link);
+static int ixj_config(struct pcmcia_device * link);
+static void ixj_cs_release(struct pcmcia_device * link);
 
-static int ixj_attach(struct pcmcia_device *p_dev)
+static int ixj_probe(struct pcmcia_device *p_dev)
 {
-       dev_link_t *link;
-
        DEBUG(0, "ixj_attach()\n");
        /* Create new ixj device */
-       link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
-       if (!link)
-               return -ENOMEM;
-       memset(link, 0, sizeof(struct dev_link_t));
-       link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-       link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
-       link->io.IOAddrLines = 3;
-       link->conf.Vcc = 50;
-       link->conf.IntType = INT_MEMORY_AND_IO;
-       link->priv = kmalloc(sizeof(struct ixj_info_t), GFP_KERNEL);
-       if (!link->priv) {
-               kfree(link);
+       p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+       p_dev->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+       p_dev->io.IOAddrLines = 3;
+       p_dev->conf.IntType = INT_MEMORY_AND_IO;
+       p_dev->priv = kmalloc(sizeof(struct ixj_info_t), GFP_KERNEL);
+       if (!p_dev->priv) {
                return -ENOMEM;
        }
-       memset(link->priv, 0, sizeof(struct ixj_info_t));
-
-       link->handle = p_dev;
-       p_dev->instance = link;
+       memset(p_dev->priv, 0, sizeof(struct ixj_info_t));
 
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       ixj_config(link);
-
-       return 0;
+       return ixj_config(p_dev);
 }
 
-static void ixj_detach(struct pcmcia_device *p_dev)
+static void ixj_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
-
        DEBUG(0, "ixj_detach(0x%p)\n", link);
 
-       link->state &= ~DEV_RELEASE_PENDING;
-       if (link->state & DEV_CONFIG)
-               ixj_cs_release(link);
+       ixj_cs_release(link);
 
         kfree(link->priv);
-        kfree(link);
 }
 
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void ixj_get_serial(dev_link_t * link, IXJ * j)
+static void ixj_get_serial(struct pcmcia_device * link, IXJ * j)
 {
-       client_handle_t handle;
        tuple_t tuple;
        u_short buf[128];
        char *str;
        int last_ret, last_fn, i, place;
-       handle = link->handle;
        DEBUG(0, "ixj_get_serial(0x%p)\n", link);
        tuple.TupleData = (cisdata_t *) buf;
        tuple.TupleOffset = 0;
        tuple.TupleDataMax = 80;
        tuple.Attributes = 0;
        tuple.DesiredTuple = CISTPL_VERS_1;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
        str = (char *) buf;
        printk("PCMCIA Version %d.%d\n", str[0], str[1]);
        str += 2;
@@ -149,22 +128,19 @@ static void ixj_get_serial(dev_link_t * link, IXJ * j)
        return;
 }
 
-static void ixj_config(dev_link_t * link)
+static int ixj_config(struct pcmcia_device * link)
 {
        IXJ *j;
-       client_handle_t handle;
        ixj_info_t *info;
        tuple_t tuple;
        u_short buf[128];
        cisparse_t parse;
-       config_info_t conf;
        cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
        cistpl_cftable_entry_t dflt =
        {
                0
        };
        int last_ret, last_fn;
-       handle = link->handle;
        info = link->priv;
        DEBUG(0, "ixj_config(0x%p)\n", link);
        tuple.TupleData = (cisdata_t *) buf;
@@ -172,19 +148,17 @@ static void ixj_config(dev_link_t * link)
        tuple.TupleDataMax = 255;
        tuple.Attributes = 0;
        tuple.DesiredTuple = CISTPL_CONFIG;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
-       link->state |= DEV_CONFIG;
-       CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
        tuple.Attributes = 0;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        while (1) {
-               if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-                               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+               if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+                               pcmcia_parse_tuple(link, &tuple, &parse) != 0)
                        goto next_entry;
                if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
                        cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
@@ -195,7 +169,7 @@ static void ixj_config(dev_link_t * link)
                                link->io.BasePort2 = io->win[1].base;
                                link->io.NumPorts2 = io->win[1].len;
                        }
-                       if (pcmcia_request_io(link->handle, &link->io) != 0)
+                       if (pcmcia_request_io(link, &link->io) != 0)
                                goto next_entry;
                        /* If we've got this far, we're done */
                        break;
@@ -203,10 +177,10 @@ static void ixj_config(dev_link_t * link)
              next_entry:
                if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
                        dflt = *cfg;
-               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
        }
 
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
        /*
         *      Register the card with the core.
@@ -215,46 +189,21 @@ static void ixj_config(dev_link_t * link)
 
        info->ndev = 1;
        info->node.major = PHONE_MAJOR;
-       link->dev = &info->node;
+       link->dev_node = &info->node;
        ixj_get_serial(link, j);
-       link->state &= ~DEV_CONFIG_PENDING;
-       return;
+       return 0;
       cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
        ixj_cs_release(link);
+       return -ENODEV;
 }
 
-static void ixj_cs_release(dev_link_t *link)
+static void ixj_cs_release(struct pcmcia_device *link)
 {
        ixj_info_t *info = link->priv;
        DEBUG(0, "ixj_cs_release(0x%p)\n", link);
        info->ndev = 0;
-       link->dev = NULL;
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       link->state &= ~DEV_CONFIG;
-}
-
-static int ixj_suspend(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int ixj_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (DEV_OK(link))
-               pcmcia_request_configuration(link->handle, &link->conf);
-
-       return 0;
+       pcmcia_disable_device(link);
 }
 
 static struct pcmcia_device_id ixj_ids[] = {
@@ -268,11 +217,9 @@ static struct pcmcia_driver ixj_driver = {
        .drv            = {
                .name   = "ixj_cs",
        },
-       .probe          = ixj_attach,
+       .probe          = ixj_probe,
        .remove         = ixj_detach,
        .id_table       = ixj_ids,
-       .suspend        = ixj_suspend,
-       .resume         = ixj_resume,
 };
 
 static int __init ixj_pcmcia_init(void)
index 134d200..302aa1e 100644 (file)
@@ -67,11 +67,11 @@ module_param(pc_debug, int, 0644);
 static const char driver_name[DEV_NAME_LEN]  = "sl811_cs";
 
 typedef struct local_info_t {
-       dev_link_t              link;
+       struct pcmcia_device    *p_dev;
        dev_node_t              node;
 } local_info_t;
 
-static void sl811_cs_release(dev_link_t * link);
+static void sl811_cs_release(struct pcmcia_device * link);
 
 /*====================================================================*/
 
@@ -138,41 +138,27 @@ static int sl811_hc_init(struct device *parent, ioaddr_t base_addr, int irq)
 
 /*====================================================================*/
 
-static void sl811_cs_detach(struct pcmcia_device *p_dev)
+static void sl811_cs_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
-
        DBG(0, "sl811_cs_detach(0x%p)\n", link);
 
-       link->state &= ~DEV_PRESENT;
-       if (link->state & DEV_CONFIG)
-               sl811_cs_release(link);
+       sl811_cs_release(link);
 
        /* This points to the parent local_info_t struct */
        kfree(link->priv);
 }
 
-static void sl811_cs_release(dev_link_t * link)
+static void sl811_cs_release(struct pcmcia_device * link)
 {
-
        DBG(0, "sl811_cs_release(0x%p)\n", link);
 
-       /* Unlink the device chain */
-       link->dev = NULL;
-
+       pcmcia_disable_device(link);
        platform_device_unregister(&platform_dev);
-       pcmcia_release_configuration(link->handle);
-       if (link->io.NumPorts1)
-               pcmcia_release_io(link->handle, &link->io);
-       if (link->irq.AssignedIRQ)
-               pcmcia_release_irq(link->handle, &link->irq);
-       link->state &= ~DEV_CONFIG;
 }
 
-static void sl811_cs_config(dev_link_t *link)
+static int sl811_cs_config(struct pcmcia_device *link)
 {
-       client_handle_t         handle = link->handle;
-       struct device           *parent = &handle_to_dev(handle);
+       struct device           *parent = &handle_to_dev(link);
        local_info_t            *dev = link->priv;
        tuple_t                 tuple;
        cisparse_t              parse;
@@ -188,27 +174,23 @@ static void sl811_cs_config(dev_link_t *link)
        tuple.TupleData = buf;
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
        /* Look up the current Vcc */
        CS_CHECK(GetConfigurationInfo,
-                       pcmcia_get_configuration_info(handle, &conf));
-       link->conf.Vcc = conf.Vcc;
+                       pcmcia_get_configuration_info(link, &conf));
 
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        while (1) {
                cistpl_cftable_entry_t  *cfg = &(parse.cftable_entry);
 
-               if (pcmcia_get_tuple_data(handle, &tuple) != 0
-                               || pcmcia_parse_tuple(handle, &tuple, &parse)
+               if (pcmcia_get_tuple_data(link, &tuple) != 0
+                               || pcmcia_parse_tuple(link, &tuple, &parse)
                                                != 0)
                        goto next_entry;
 
@@ -234,10 +216,10 @@ static void sl811_cs_config(dev_link_t *link)
                }
 
                if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
                else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
 
                /* we need an interrupt */
@@ -254,15 +236,14 @@ static void sl811_cs_config(dev_link_t *link)
                        link->io.BasePort1 = io->win[0].base;
                        link->io.NumPorts1 = io->win[0].len;
 
-                       if (pcmcia_request_io(link->handle, &link->io) != 0)
+                       if (pcmcia_request_io(link, &link->io) != 0)
                                goto next_entry;
                }
                break;
 
 next_entry:
-               if (link->io.NumPorts1)
-                       pcmcia_release_io(link->handle, &link->io);
-               last_ret = pcmcia_get_next_tuple(handle, &tuple);
+               pcmcia_disable_device(link);
+               last_ret = pcmcia_get_next_tuple(link, &tuple);
        }
 
        /* require an IRQ and two registers */
@@ -270,71 +251,46 @@ next_entry:
                goto cs_failed;
        if (link->conf.Attributes & CONF_ENABLE_IRQ)
                CS_CHECK(RequestIRQ,
-                       pcmcia_request_irq(link->handle, &link->irq));
+                       pcmcia_request_irq(link, &link->irq));
        else
                goto cs_failed;
 
        CS_CHECK(RequestConfiguration,
-               pcmcia_request_configuration(link->handle, &link->conf));
+               pcmcia_request_configuration(link, &link->conf));
 
        sprintf(dev->node.dev_name, driver_name);
        dev->node.major = dev->node.minor = 0;
-       link->dev = &dev->node;
+       link->dev_node = &dev->node;
 
-       printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
-              dev->node.dev_name, link->conf.ConfigIndex,
-              link->conf.Vcc/10, link->conf.Vcc%10);
-       if (link->conf.Vpp1)
-               printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+       printk(KERN_INFO "%s: index 0x%02x: ",
+              dev->node.dev_name, link->conf.ConfigIndex);
+       if (link->conf.Vpp)
+               printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10);
        printk(", irq %d", link->irq.AssignedIRQ);
        printk(", io 0x%04x-0x%04x", link->io.BasePort1,
               link->io.BasePort1+link->io.NumPorts1-1);
        printk("\n");
 
-       link->state &= ~DEV_CONFIG_PENDING;
-
        if (sl811_hc_init(parent, link->io.BasePort1, link->irq.AssignedIRQ)
                        < 0) {
 cs_failed:
                printk("sl811_cs_config failed\n");
-               cs_error(link->handle, last_fn, last_ret);
+               cs_error(link, last_fn, last_ret);
                sl811_cs_release(link);
-               link->state &= ~DEV_CONFIG_PENDING;
+               return  -ENODEV;
        }
-}
-
-static int sl811_suspend(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state |= DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
-       return 0;
-}
-
-static int sl811_resume(struct pcmcia_device *dev)
-{
-       dev_link_t *link = dev_to_instance(dev);
-
-       link->state &= ~DEV_SUSPEND;
-       if (link->state & DEV_CONFIG)
-               pcmcia_request_configuration(link->handle, &link->conf);
-
        return 0;
 }
 
-static int sl811_cs_attach(struct pcmcia_device *p_dev)
+static int sl811_cs_probe(struct pcmcia_device *link)
 {
        local_info_t *local;
-       dev_link_t *link;
 
        local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
        if (!local)
                return -ENOMEM;
        memset(local, 0, sizeof(local_info_t));
-       link = &local->link;
+       local->p_dev = link;
        link->priv = local;
 
        /* Initialize */
@@ -343,16 +299,9 @@ static int sl811_cs_attach(struct pcmcia_device *p_dev)
        link->irq.Handler = NULL;
 
        link->conf.Attributes = 0;
-       link->conf.Vcc = 33;
        link->conf.IntType = INT_MEMORY_AND_IO;
 
-       link->handle = p_dev;
-       p_dev->instance = link;
-
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       sl811_cs_config(link);
-
-       return 0;
+       return sl811_cs_config(link);
 }
 
 static struct pcmcia_device_id sl811_ids[] = {
@@ -366,11 +315,9 @@ static struct pcmcia_driver sl811_cs_driver = {
        .drv            = {
                .name   = (char *)driver_name,
        },
-       .probe          = sl811_cs_attach,
+       .probe          = sl811_cs_probe,
        .remove         = sl811_cs_detach,
        .id_table       = sl811_ids,
-       .suspend        = sl811_suspend,
-       .resume         = sl811_resume,
 };
 
 /*====================================================================*/
index cb0d80f..25bc85f 100644 (file)
@@ -510,7 +510,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                                case 0x025: map_key_clear(KEY_TV);              break;
                                case 0x026: map_key_clear(KEY_MENU);            break;
                                case 0x031: map_key_clear(KEY_AUDIO);           break;
-                               case 0x032: map_key_clear(KEY_SUBTITLE);        break;
+                               case 0x032: map_key_clear(KEY_TEXT);            break;
                                case 0x033: map_key_clear(KEY_LAST);            break;
                                case 0x047: map_key_clear(KEY_MP3);             break;
                                case 0x048: map_key_clear(KEY_DVD);             break;
index 910a8ed..b05d1b2 100644 (file)
@@ -929,8 +929,7 @@ do_holes:
                        block_in_page += this_chunk_blocks;
                        dio->blocks_available -= this_chunk_blocks;
 next_block:
-                       if (dio->block_in_file > dio->final_block_in_request)
-                               BUG();
+                       BUG_ON(dio->block_in_file > dio->final_block_in_request);
                        if (dio->block_in_file == dio->final_block_in_request)
                                break;
                }
index 6b38869..81d87a4 100644 (file)
@@ -590,8 +590,7 @@ we_slept:
        atomic_dec(&dquot->dq_count);
 #ifdef __DQUOT_PARANOIA
        /* sanity check */
-       if (!list_empty(&dquot->dq_free))
-               BUG();
+       BUG_ON(!list_empty(&dquot->dq_free));
 #endif
        put_dquot_last(dquot);
        spin_unlock(&dq_list_lock);
@@ -666,8 +665,7 @@ we_slept:
                return NODQUOT;
        }
 #ifdef __DQUOT_PARANOIA
-       if (!dquot->dq_sb)      /* Has somebody invalidated entry under us? */
-               BUG();
+       BUG_ON(!dquot->dq_sb);  /* Has somebody invalidated entry under us? */
 #endif
 
        return dquot;
index 950ebd4..0291a68 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -561,7 +561,7 @@ static int exec_mmap(struct mm_struct *mm)
        arch_pick_mmap_layout(mm);
        if (old_mm) {
                up_read(&old_mm->mmap_sem);
-               if (active_mm != old_mm) BUG();
+               BUG_ON(active_mm != old_mm);
                mmput(old_mm);
                return 0;
        }
index 2a24791..d35cbc6 100644 (file)
@@ -453,8 +453,7 @@ static void send_sigio_to_task(struct task_struct *p,
                        /* Make sure we are called with one of the POLL_*
                           reasons, otherwise we could leak kernel stack into
                           userspace.  */
-                       if ((reason & __SI_MASK) != __SI_POLL)
-                               BUG();
+                       BUG_ON((reason & __SI_MASK) != __SI_POLL);
                        if (reason - POLL_IN >= NSIGPOLL)
                                si.si_band  = ~0L;
                        else
index 76a0708..0495008 100644 (file)
 static inline void
 vxfs_get_fshead(struct vxfs_oltfshead *fshp, struct vxfs_sb_info *infp)
 {
-       if (infp->vsi_fshino)
-               BUG();
+       BUG_ON(infp->vsi_fshino);
        infp->vsi_fshino = fshp->olt_fsino[0];
 }
 
 static inline void
 vxfs_get_ilist(struct vxfs_oltilist *ilistp, struct vxfs_sb_info *infp)
 {
-       if (infp->vsi_iext)
-               BUG();
+       BUG_ON(infp->vsi_iext);
        infp->vsi_iext = ilistp->olt_iext[0]; 
 }
 
 static inline u_long
 vxfs_oblock(struct super_block *sbp, daddr_t block, u_long bsize)
 {
-       if (sbp->s_blocksize % bsize)
-               BUG();
+       BUG_ON(sbp->s_blocksize % bsize);
        return (block * (sbp->s_blocksize / bsize));
 }
 
index 8f07e8f..746abc9 100644 (file)
@@ -466,8 +466,7 @@ void hfs_bnode_unhash(struct hfs_bnode *node)
        for (p = &node->tree->node_hash[hfs_bnode_hash(node->this)];
             *p && *p != node; p = &(*p)->next_hash)
                ;
-       if (!*p)
-               BUG();
+       BUG_ON(!*p);
        *p = node->next_hash;
        node->tree->node_hash_cnt--;
 }
@@ -622,8 +621,7 @@ void hfs_bnode_put(struct hfs_bnode *node)
 
                dprint(DBG_BNODE_REFS, "put_node(%d:%d): %d\n",
                       node->tree->cnid, node->this, atomic_read(&node->refcnt));
-               if (!atomic_read(&node->refcnt))
-                       BUG();
+               BUG_ON(!atomic_read(&node->refcnt));
                if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock))
                        return;
                for (i = 0; i < tree->pages_per_bnode; i++) {
index a67edfa..effa899 100644 (file)
@@ -269,8 +269,7 @@ void hfs_bmap_free(struct hfs_bnode *node)
        u8 *data, byte, m;
 
        dprint(DBG_BNODE_MOD, "btree_free_node: %u\n", node->this);
-       if (!node->this)
-               BUG();
+       BUG_ON(!node->this);
        tree = node->tree;
        nidx = node->this;
        node = hfs_bnode_find(tree, 0);
index 32b7c33..3a2446a 100644 (file)
@@ -172,8 +172,7 @@ static struct inode *alloc_inode(struct super_block *sb)
 
 void destroy_inode(struct inode *inode) 
 {
-       if (inode_has_buffers(inode))
-               BUG();
+       BUG_ON(inode_has_buffers(inode));
        security_inode_free(inode);
        if (inode->i_sb->s_op->destroy_inode)
                inode->i_sb->s_op->destroy_inode(inode);
@@ -249,12 +248,9 @@ void clear_inode(struct inode *inode)
        might_sleep();
        invalidate_inode_buffers(inode);
        
-       if (inode->i_data.nrpages)
-               BUG();
-       if (!(inode->i_state & I_FREEING))
-               BUG();
-       if (inode->i_state & I_CLEAR)
-               BUG();
+       BUG_ON(inode->i_data.nrpages);
+       BUG_ON(!(inode->i_state & I_FREEING));
+       BUG_ON(inode->i_state & I_CLEAR);
        wait_on_inode(inode);
        DQUOT_DROP(inode);
        if (inode->i_sb && inode->i_sb->s_op->clear_inode)
@@ -1054,8 +1050,7 @@ void generic_delete_inode(struct inode *inode)
        hlist_del_init(&inode->i_hash);
        spin_unlock(&inode_lock);
        wake_up_inode(inode);
-       if (inode->i_state != I_CLEAR)
-               BUG();
+       BUG_ON(inode->i_state != I_CLEAR);
        destroy_inode(inode);
 }
 
index 7b77a95..ff2a872 100644 (file)
@@ -35,8 +35,7 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
        pid_t pid;
        int ret = 0;
 
-       if (c->gc_task)
-               BUG();
+       BUG_ON(c->gc_task);
 
        init_completion(&c->gc_thread_start);
        init_completion(&c->gc_thread_exit);
index c56bd99..ed9a24d 100644 (file)
@@ -178,11 +178,9 @@ smb_writepage(struct page *page, struct writeback_control *wbc)
        unsigned offset = PAGE_CACHE_SIZE;
        int err;
 
-       if (!mapping)
-               BUG();
+       BUG_ON(!mapping);
        inode = mapping->host;
-       if (!inode)
-               BUG();
+       BUG_ON(!inode);
 
        end_index = inode->i_size >> PAGE_CACHE_SHIFT;
 
index 7c2bbf1..6081cf7 100644 (file)
@@ -106,7 +106,7 @@ static struct pipe_buf_operations page_cache_pipe_buf_ops = {
 
 static ssize_t move_to_pipe(struct inode *inode, struct page **pages,
                            int nr_pages, unsigned long offset,
-                           unsigned long len)
+                           unsigned long len, unsigned int flags)
 {
        struct pipe_inode_info *info;
        int ret, do_wakeup, i;
@@ -159,6 +159,12 @@ static ssize_t move_to_pipe(struct inode *inode, struct page **pages,
                        break;
                }
 
+               if (flags & SPLICE_F_NONBLOCK) {
+                       if (!ret)
+                               ret = -EAGAIN;
+                       break;
+               }
+
                if (signal_pending(current)) {
                        if (!ret)
                                ret = -ERESTARTSYS;
@@ -191,7 +197,7 @@ static ssize_t move_to_pipe(struct inode *inode, struct page **pages,
 }
 
 static int __generic_file_splice_read(struct file *in, struct inode *pipe,
-                                     size_t len)
+                                     size_t len, unsigned int flags)
 {
        struct address_space *mapping = in->f_mapping;
        unsigned int offset, nr_pages;
@@ -279,7 +285,7 @@ static int __generic_file_splice_read(struct file *in, struct inode *pipe,
         * Now we splice them into the pipe..
         */
 splice_them:
-       return move_to_pipe(pipe, pages, i, offset, len);
+       return move_to_pipe(pipe, pages, i, offset, len, flags);
 }
 
 ssize_t generic_file_splice_read(struct file *in, struct inode *pipe,
@@ -291,7 +297,7 @@ ssize_t generic_file_splice_read(struct file *in, struct inode *pipe,
        ret = 0;
        spliced = 0;
        while (len) {
-               ret = __generic_file_splice_read(in, pipe, len);
+               ret = __generic_file_splice_read(in, pipe, len, flags);
 
                if (ret <= 0)
                        break;
@@ -299,6 +305,11 @@ ssize_t generic_file_splice_read(struct file *in, struct inode *pipe,
                in->f_pos += ret;
                len -= ret;
                spliced += ret;
+
+               if (!(flags & SPLICE_F_NONBLOCK))
+                       continue;
+               ret = -EAGAIN;
+               break;
        }
 
        if (spliced)
@@ -527,6 +538,12 @@ static ssize_t move_from_pipe(struct inode *inode, struct file *out,
                                break;
                }
 
+               if (flags & SPLICE_F_NONBLOCK) {
+                       if (!ret)
+                               ret = -EAGAIN;
+                       break;
+               }
+
                if (signal_pending(current)) {
                        if (!ret)
                                ret = -ERESTARTSYS;
index f26880a..6cfdc9a 100644 (file)
@@ -50,7 +50,7 @@ static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd,
        return sd;
 }
 
-/**
+/*
  *
  * Return -EEXIST if there is already a sysfs element with the same name for
  * the same parent.
index 830f76f..f1cb1dd 100644 (file)
@@ -183,7 +183,7 @@ fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t
                return -ENOMEM;
 
        if (count >= PAGE_SIZE)
-               count = PAGE_SIZE;
+               count = PAGE_SIZE - 1;
        error = copy_from_user(buffer->page,buf,count);
        buffer->needs_read_fill = 1;
        return error ? -EFAULT : count;
index 4c29ac4..f0b347b 100644 (file)
@@ -175,8 +175,7 @@ const unsigned char * sysfs_get_name(struct sysfs_dirent *sd)
        struct bin_attribute * bin_attr;
        struct sysfs_symlink  * sl;
 
-       if (!sd || !sd->s_element)
-               BUG();
+       BUG_ON(!sd || !sd->s_element);
 
        switch (sd->s_type) {
                case SYSFS_DIR:
index 8c66e92..d707434 100644 (file)
@@ -253,8 +253,7 @@ int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page)
 
        lock_page(page);
        err = mapping->a_ops->prepare_write(NULL, page, from, to);
-       if (err)
-               BUG();
+       BUG_ON(err);
        de->inode = 0;
        err = dir_commit_chunk(page, from, to);
        dir_put_page(page);
@@ -353,8 +352,7 @@ void sysv_set_link(struct sysv_dir_entry *de, struct page *page,
 
        lock_page(page);
        err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
-       if (err)
-               BUG();
+       BUG_ON(err);
        de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino);
        err = dir_commit_chunk(page, from, to);
        dir_put_page(page);
index 81e0e84..2983afd 100644 (file)
@@ -312,12 +312,10 @@ static int udf_get_block(struct inode *inode, sector_t block, struct buffer_head
        err = 0;
 
        bh = inode_getblk(inode, block, &err, &phys, &new);
-       if (bh)
-               BUG();
+       BUG_ON(bh);
        if (err)
                goto abort;
-       if (!phys)
-               BUG();
+       BUG_ON(!phys);
 
        if (new)
                set_buffer_new(bh_result);
index 85997b1..ae4c475 100644 (file)
@@ -69,7 +69,6 @@ __xfs_file_read(
        return rval;
 }
 
-
 STATIC ssize_t
 xfs_file_aio_read(
        struct kiocb            *iocb,
@@ -90,7 +89,6 @@ xfs_file_aio_read_invis(
        return __xfs_file_read(iocb, buf, IO_ISAIO|IO_INVIS, count, pos);
 }
 
-
 STATIC inline ssize_t
 __xfs_file_write(
        struct kiocb    *iocb,
@@ -113,7 +111,6 @@ __xfs_file_write(
        return rval;
 }
 
-
 STATIC ssize_t
 xfs_file_aio_write(
        struct kiocb            *iocb,
@@ -134,7 +131,6 @@ xfs_file_aio_write_invis(
        return __xfs_file_write(iocb, buf, IO_ISAIO|IO_INVIS, count, pos);
 }
 
-
 STATIC inline ssize_t
 __xfs_file_readv(
        struct file             *file,
@@ -179,7 +175,6 @@ xfs_file_readv_invis(
        return __xfs_file_readv(file, iov, IO_INVIS, nr_segs, ppos);
 }
 
-
 STATIC inline ssize_t
 __xfs_file_writev(
        struct file             *file,
@@ -204,7 +199,6 @@ __xfs_file_writev(
        return rval;
 }
 
-
 STATIC ssize_t
 xfs_file_writev(
        struct file             *file,
@@ -228,7 +222,7 @@ xfs_file_writev_invis(
 STATIC ssize_t
 xfs_file_sendfile(
        struct file             *filp,
-       loff_t                  *ppos,
+       loff_t                  *pos,
        size_t                  count,
        read_actor_t            actor,
        void                    *target)
@@ -236,10 +230,80 @@ xfs_file_sendfile(
        vnode_t                 *vp = vn_from_inode(filp->f_dentry->d_inode);
        ssize_t                 rval;
 
-       VOP_SENDFILE(vp, filp, ppos, 0, count, actor, target, NULL, rval);
+       VOP_SENDFILE(vp, filp, pos, 0, count, actor, target, NULL, rval);
        return rval;
 }
 
+STATIC ssize_t
+xfs_file_sendfile_invis(
+       struct file             *filp,
+       loff_t                  *pos,
+       size_t                  count,
+       read_actor_t            actor,
+       void                    *target)
+{
+       vnode_t                 *vp = vn_from_inode(filp->f_dentry->d_inode);
+       ssize_t                 rval;
+
+       VOP_SENDFILE(vp, filp, pos, IO_INVIS, count, actor, target, NULL, rval);
+       return rval;
+}
+
+STATIC ssize_t
+xfs_file_splice_read(
+       struct file             *infilp,
+       struct inode            *pipe,
+       size_t                  len,
+       unsigned int            flags)
+{
+       vnode_t                 *vp = vn_from_inode(infilp->f_dentry->d_inode);
+       ssize_t                 rval;
+
+       VOP_SPLICE_READ(vp, infilp, pipe, len, flags, 0, NULL, rval);
+       return rval;
+}
+
+STATIC ssize_t
+xfs_file_splice_read_invis(
+       struct file             *infilp,
+       struct inode            *pipe,
+       size_t                  len,
+       unsigned int            flags)
+{
+       vnode_t                 *vp = vn_from_inode(infilp->f_dentry->d_inode);
+       ssize_t                 rval;
+
+       VOP_SPLICE_READ(vp, infilp, pipe, len, flags, IO_INVIS, NULL, rval);
+       return rval;
+}
+
+STATIC ssize_t
+xfs_file_splice_write(
+       struct inode            *pipe,
+       struct file             *outfilp,
+       size_t                  len,
+       unsigned int            flags)
+{
+       vnode_t                 *vp = vn_from_inode(outfilp->f_dentry->d_inode);
+       ssize_t                 rval;
+
+       VOP_SPLICE_WRITE(vp, pipe, outfilp, len, flags, 0, NULL, rval);
+       return rval;
+}
+
+STATIC ssize_t
+xfs_file_splice_write_invis(
+       struct inode            *pipe,
+       struct file             *outfilp,
+       size_t                  len,
+       unsigned int            flags)
+{
+       vnode_t                 *vp = vn_from_inode(outfilp->f_dentry->d_inode);
+       ssize_t                 rval;
+
+       VOP_SPLICE_WRITE(vp, pipe, outfilp, len, flags, IO_INVIS, NULL, rval);
+       return rval;
+}
 
 STATIC int
 xfs_file_open(
@@ -251,13 +315,10 @@ xfs_file_open(
 
        if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
                return -EFBIG;
-
-       ASSERT(vp);
        VOP_OPEN(vp, NULL, error);
        return -error;
 }
 
-
 STATIC int
 xfs_file_release(
        struct inode    *inode,
@@ -271,7 +332,6 @@ xfs_file_release(
        return -error;
 }
 
-
 STATIC int
 xfs_file_fsync(
        struct file     *filp,
@@ -285,21 +345,11 @@ xfs_file_fsync(
 
        if (datasync)
                flags |= FSYNC_DATA;
-
-       ASSERT(vp);
        VOP_FSYNC(vp, flags, NULL, (xfs_off_t)0, (xfs_off_t)-1, error);
        return -error;
 }
 
-/*
- * xfs_file_readdir maps to VOP_READDIR().
- * We need to build a uio, cred, ...
- */
-
-#define nextdp(dp)      ((struct xfs_dirent *)((char *)(dp) + (dp)->d_reclen))
-
 #ifdef CONFIG_XFS_DMAPI
-
 STATIC struct page *
 xfs_vm_nopage(
        struct vm_area_struct   *area,
@@ -319,10 +369,8 @@ xfs_vm_nopage(
 
        return filemap_nopage(area, address, type);
 }
-
 #endif /* CONFIG_XFS_DMAPI */
 
-
 STATIC int
 xfs_file_readdir(
        struct file     *filp,
@@ -330,7 +378,7 @@ xfs_file_readdir(
        filldir_t       filldir)
 {
        int             error = 0;
-       vnode_t         *vp;
+       vnode_t         *vp = vn_from_inode(filp->f_dentry->d_inode);
        uio_t           uio;
        iovec_t         iov;
        int             eof = 0;
@@ -340,9 +388,6 @@ xfs_file_readdir(
        xfs_off_t       start_offset, curr_offset;
        xfs_dirent_t    *dbp = NULL;
 
-       vp = vn_from_inode(filp->f_dentry->d_inode);
-       ASSERT(vp);
-
        /* Try fairly hard to get memory */
        do {
                if ((read_buf = (caddr_t)kmalloc(rlen, GFP_KERNEL)))
@@ -387,7 +432,7 @@ xfs_file_readdir(
                        }
                        size -= dbp->d_reclen;
                        curr_offset = (loff_t)dbp->d_off /* & 0x7fffffff */;
-                       dbp = nextdp(dbp);
+                       dbp = (xfs_dirent_t *)((char *)dbp + dbp->d_reclen);
                }
        }
 done:
@@ -402,7 +447,6 @@ done:
        return -error;
 }
 
-
 STATIC int
 xfs_file_mmap(
        struct file     *filp,
@@ -457,11 +501,10 @@ xfs_file_ioctl_invis(
        unsigned int    cmd,
        unsigned long   arg)
 {
-       int             error;
        struct inode    *inode = filp->f_dentry->d_inode;
        vnode_t         *vp = vn_from_inode(inode);
+       int             error;
 
-       ASSERT(vp);
        VOP_IOCTL(vp, inode, filp, IO_INVIS, cmd, (void __user *)arg, error);
        VMODIFY(vp);
 
@@ -537,6 +580,8 @@ const struct file_operations xfs_file_operations = {
        .aio_read       = xfs_file_aio_read,
        .aio_write      = xfs_file_aio_write,
        .sendfile       = xfs_file_sendfile,
+       .splice_read    = xfs_file_splice_read,
+       .splice_write   = xfs_file_splice_write,
        .unlocked_ioctl = xfs_file_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = xfs_file_compat_ioctl,
@@ -558,7 +603,9 @@ const struct file_operations xfs_invis_file_operations = {
        .writev         = xfs_file_writev_invis,
        .aio_read       = xfs_file_aio_read_invis,
        .aio_write      = xfs_file_aio_write_invis,
-       .sendfile       = xfs_file_sendfile,
+       .sendfile       = xfs_file_sendfile_invis,
+       .splice_read    = xfs_file_splice_read_invis,
+       .splice_write   = xfs_file_splice_write_invis,
        .unlocked_ioctl = xfs_file_ioctl_invis,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = xfs_file_compat_invis_ioctl,
index 1fe09f2..e9fe43d 100644 (file)
  */
 #undef  HAVE_REFCACHE  /* reference cache not needed for NFS in 2.6 */
 #define HAVE_SENDFILE  /* sendfile(2) exists in 2.6, but not in 2.4 */
+#define HAVE_SPLICE    /* a splice(2) exists in 2.6, but not in 2.4 */
 #ifdef CONFIG_SMP
 #define HAVE_PERCPU_SB /* per cpu superblock counters are a 2.6 feature */
 #else
index 84ddf18..90cd314 100644 (file)
@@ -301,36 +301,23 @@ xfs_sendfile(
        void                    *target,
        cred_t                  *credp)
 {
+       xfs_inode_t             *ip = XFS_BHVTOI(bdp);
+       xfs_mount_t             *mp = ip->i_mount;
        ssize_t                 ret;
-       xfs_fsize_t             n;
-       xfs_inode_t             *ip;
-       xfs_mount_t             *mp;
-       vnode_t                 *vp;
-
-       ip = XFS_BHVTOI(bdp);
-       vp = BHV_TO_VNODE(bdp);
-       mp = ip->i_mount;
 
        XFS_STATS_INC(xs_read_calls);
-
-       n = XFS_MAXIOFFSET(mp) - *offset;
-       if ((n <= 0) || (count == 0))
-               return 0;
-
-       if (n < count)
-               count = n;
-
-       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+       if (XFS_FORCED_SHUTDOWN(mp))
                return -EIO;
 
        xfs_ilock(ip, XFS_IOLOCK_SHARED);
 
-       if (DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_READ) &&
+       if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) &&
            (!(ioflags & IO_INVIS))) {
                vrwlock_t locktype = VRWLOCK_READ;
                int error;
 
-               error = XFS_SEND_DATA(mp, DM_EVENT_READ, BHV_TO_VNODE(bdp), *offset, count,
+               error = XFS_SEND_DATA(mp, DM_EVENT_READ, BHV_TO_VNODE(bdp),
+                                     *offset, count,
                                      FILP_DELAY_FLAG(filp), &locktype);
                if (error) {
                        xfs_iunlock(ip, XFS_IOLOCK_SHARED);
@@ -340,12 +327,96 @@ xfs_sendfile(
        xfs_rw_enter_trace(XFS_SENDFILE_ENTER, &ip->i_iocore,
                   (void *)(unsigned long)target, count, *offset, ioflags);
        ret = generic_file_sendfile(filp, offset, count, actor, target);
+       if (ret > 0)
+               XFS_STATS_ADD(xs_read_bytes, ret);
 
        xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+       return ret;
+}
 
+ssize_t
+xfs_splice_read(
+       bhv_desc_t              *bdp,
+       struct file             *infilp,
+       struct inode            *pipe,
+       size_t                  count,
+       int                     flags,
+       int                     ioflags,
+       cred_t                  *credp)
+{
+       xfs_inode_t             *ip = XFS_BHVTOI(bdp);
+       xfs_mount_t             *mp = ip->i_mount;
+       ssize_t                 ret;
+
+       XFS_STATS_INC(xs_read_calls);
+       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+               return -EIO;
+
+       xfs_ilock(ip, XFS_IOLOCK_SHARED);
+
+       if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) &&
+           (!(ioflags & IO_INVIS))) {
+               vrwlock_t locktype = VRWLOCK_READ;
+               int error;
+
+               error = XFS_SEND_DATA(mp, DM_EVENT_READ, BHV_TO_VNODE(bdp),
+                                       infilp->f_pos, count,
+                                       FILP_DELAY_FLAG(infilp), &locktype);
+               if (error) {
+                       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+                       return -error;
+               }
+       }
+       xfs_rw_enter_trace(XFS_SPLICE_READ_ENTER, &ip->i_iocore,
+                          pipe, count, infilp->f_pos, ioflags);
+       ret = generic_file_splice_read(infilp, pipe, count, flags);
        if (ret > 0)
                XFS_STATS_ADD(xs_read_bytes, ret);
 
+       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+       return ret;
+}
+
+ssize_t
+xfs_splice_write(
+       bhv_desc_t              *bdp,
+       struct inode            *pipe,
+       struct file             *outfilp,
+       size_t                  count,
+       int                     flags,
+       int                     ioflags,
+       cred_t                  *credp)
+{
+       xfs_inode_t             *ip = XFS_BHVTOI(bdp);
+       xfs_mount_t             *mp = ip->i_mount;
+       ssize_t                 ret;
+
+       XFS_STATS_INC(xs_write_calls);
+       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+               return -EIO;
+
+       xfs_ilock(ip, XFS_IOLOCK_EXCL);
+
+       if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_WRITE) &&
+           (!(ioflags & IO_INVIS))) {
+               vrwlock_t locktype = VRWLOCK_WRITE;
+               int error;
+
+               error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, BHV_TO_VNODE(bdp),
+                                       outfilp->f_pos, count,
+                                       FILP_DELAY_FLAG(outfilp), &locktype);
+               if (error) {
+                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+                       return -error;
+               }
+       }
+       xfs_rw_enter_trace(XFS_SPLICE_WRITE_ENTER, &ip->i_iocore,
+                          pipe, count, outfilp->f_pos, ioflags);
+       ret = generic_file_splice_write(pipe, outfilp, count, flags);
+       if (ret > 0)
+               XFS_STATS_ADD(xs_write_bytes, ret);
+
+       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
        return ret;
 }
 
@@ -363,7 +434,7 @@ xfs_zero_last_block(
        xfs_fsize_t     end_size)
 {
        xfs_fileoff_t   last_fsb;
-       xfs_mount_t     *mp;
+       xfs_mount_t     *mp = io->io_mount;
        int             nimaps;
        int             zero_offset;
        int             zero_len;
@@ -373,8 +444,6 @@ xfs_zero_last_block(
 
        ASSERT(ismrlocked(io->io_lock, MR_UPDATE) != 0);
 
-       mp = io->io_mount;
-
        zero_offset = XFS_B_FSB_OFFSET(mp, isize);
        if (zero_offset == 0) {
                /*
@@ -405,10 +474,9 @@ xfs_zero_last_block(
         * don't deadlock when the buffer cache calls back to us.
         */
        XFS_IUNLOCK(mp, io, XFS_ILOCK_EXCL| XFS_EXTSIZE_RD);
-       loff = XFS_FSB_TO_B(mp, last_fsb);
 
+       loff = XFS_FSB_TO_B(mp, last_fsb);
        zero_len = mp->m_sb.sb_blocksize - zero_offset;
-
        error = xfs_iozero(ip, loff + zero_offset, zero_len, end_size);
 
        XFS_ILOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD);
@@ -441,7 +509,7 @@ xfs_zero_eof(
        xfs_fileoff_t   zero_count_fsb;
        xfs_fileoff_t   last_fsb;
        xfs_extlen_t    buf_len_fsb;
-       xfs_mount_t     *mp;
+       xfs_mount_t     *mp = io->io_mount;
        int             nimaps;
        int             error = 0;
        xfs_bmbt_irec_t imap;
@@ -450,8 +518,6 @@ xfs_zero_eof(
        ASSERT(ismrlocked(io->io_iolock, MR_UPDATE));
        ASSERT(offset > isize);
 
-       mp = io->io_mount;
-
        /*
         * First handle zeroing the block on which isize resides.
         * We only zero a part of that block so it is handled specially.
index 38864a8..eaa5659 100644 (file)
@@ -60,6 +60,8 @@ struct xfs_iomap;
 #define        XFS_IOMAP_ALLOC_ENTER   25
 #define        XFS_IOMAP_ALLOC_MAP     26
 #define        XFS_IOMAP_UNWRITTEN     27
+#define XFS_SPLICE_READ_ENTER  28
+#define XFS_SPLICE_WRITE_ENTER 29
 extern void xfs_rw_enter_trace(int, struct xfs_iocore *,
                                void *, size_t, loff_t, int);
 extern void xfs_inval_cached_trace(struct xfs_iocore *,
@@ -78,6 +80,7 @@ extern int xfs_bmap(struct bhv_desc *, xfs_off_t, ssize_t, int,
                        struct xfs_iomap *, int *);
 extern int xfsbdstrat(struct xfs_mount *, struct xfs_buf *);
 extern int xfs_bdstrat_cb(struct xfs_buf *);
+extern int xfs_dev_is_read_only(struct xfs_mount *, char *);
 
 extern int xfs_zero_eof(struct vnode *, struct xfs_iocore *, xfs_off_t,
                                xfs_fsize_t, xfs_fsize_t);
@@ -90,7 +93,11 @@ extern ssize_t xfs_write(struct bhv_desc *, struct kiocb *,
 extern ssize_t xfs_sendfile(struct bhv_desc *, struct file *,
                                loff_t *, int, size_t, read_actor_t,
                                void *, struct cred *);
-
-extern int xfs_dev_is_read_only(struct xfs_mount *, char *);
+extern ssize_t xfs_splice_read(struct bhv_desc *, struct file *,
+                               struct inode *, size_t, int, int,
+                               struct cred *);
+extern ssize_t xfs_splice_write(struct bhv_desc *, struct inode *,
+                               struct file *, size_t, int, int,
+                               struct cred *);
 
 #endif /* __XFS_LRW_H__ */
index 1884300..68f4793 100644 (file)
@@ -67,7 +67,8 @@ mempool_t *xfs_ioend_pool;
 
 STATIC struct xfs_mount_args *
 xfs_args_allocate(
-       struct super_block      *sb)
+       struct super_block      *sb,
+       int                     silent)
 {
        struct xfs_mount_args   *args;
 
@@ -80,8 +81,8 @@ xfs_args_allocate(
                args->flags |= XFSMNT_DIRSYNC;
        if (sb->s_flags & MS_SYNCHRONOUS)
                args->flags |= XFSMNT_WSYNC;
-
-       /* Default to 32 bit inodes on Linux all the time */
+       if (silent)
+               args->flags |= XFSMNT_QUIET;
        args->flags |= XFSMNT_32BITINODES;
 
        return args;
@@ -719,7 +720,7 @@ xfs_fs_remount(
        char                    *options)
 {
        vfs_t                   *vfsp = vfs_from_sb(sb);
-       struct xfs_mount_args   *args = xfs_args_allocate(sb);
+       struct xfs_mount_args   *args = xfs_args_allocate(sb, 0);
        int                     error;
 
        VFS_PARSEARGS(vfsp, options, args, 1, error);
@@ -825,7 +826,7 @@ xfs_fs_fill_super(
 {
        vnode_t                 *rootvp;
        struct vfs              *vfsp = vfs_allocate(sb);
-       struct xfs_mount_args   *args = xfs_args_allocate(sb);
+       struct xfs_mount_args   *args = xfs_args_allocate(sb, silent);
        struct kstatfs          statvfs;
        int                     error, error2;
 
index 06f5845..6f1c79a 100644 (file)
@@ -173,6 +173,12 @@ typedef ssize_t (*vop_write_t)(bhv_desc_t *, struct kiocb *,
 typedef ssize_t (*vop_sendfile_t)(bhv_desc_t *, struct file *,
                                loff_t *, int, size_t, read_actor_t,
                                void *, struct cred *);
+typedef ssize_t (*vop_splice_read_t)(bhv_desc_t *, struct file *,
+                               struct inode *, size_t, int, int,
+                               struct cred *);
+typedef ssize_t (*vop_splice_write_t)(bhv_desc_t *, struct inode *,
+                               struct file *, size_t, int, int,
+                               struct cred *);
 typedef int    (*vop_ioctl_t)(bhv_desc_t *, struct inode *, struct file *,
                                int, unsigned int, void __user *);
 typedef int    (*vop_getattr_t)(bhv_desc_t *, struct vattr *, int,
@@ -231,6 +237,8 @@ typedef struct vnodeops {
        vop_read_t              vop_read;
        vop_write_t             vop_write;
        vop_sendfile_t          vop_sendfile;
+       vop_splice_read_t       vop_splice_read;
+       vop_splice_write_t      vop_splice_write;
        vop_ioctl_t             vop_ioctl;
        vop_getattr_t           vop_getattr;
        vop_setattr_t           vop_setattr;
@@ -276,6 +284,10 @@ typedef struct vnodeops {
        rv = _VOP_(vop_write, vp)((vp)->v_fbhv,file,iov,segs,offset,ioflags,cr)
 #define VOP_SENDFILE(vp,f,off,ioflags,cnt,act,targ,cr,rv)              \
        rv = _VOP_(vop_sendfile, vp)((vp)->v_fbhv,f,off,ioflags,cnt,act,targ,cr)
+#define VOP_SPLICE_READ(vp,f,pipe,cnt,fl,iofl,cr,rv)                   \
+       rv = _VOP_(vop_splice_read, vp)((vp)->v_fbhv,f,pipe,cnt,fl,iofl,cr)
+#define VOP_SPLICE_WRITE(vp,f,pipe,cnt,fl,iofl,cr,rv)                  \
+       rv = _VOP_(vop_splice_write, vp)((vp)->v_fbhv,f,pipe,cnt,fl,iofl,cr)
 #define VOP_BMAP(vp,of,sz,rw,b,n,rv)                                   \
        rv = _VOP_(vop_bmap, vp)((vp)->v_fbhv,of,sz,rw,b,n)
 #define VOP_OPEN(vp, cr, rv)                                           \
index 73c1e5e..7fb5eca 100644 (file)
@@ -2624,7 +2624,7 @@ xfs_qm_vop_chown_reserve(
 {
        int             error;
        xfs_mount_t     *mp;
-       uint            delblks, blkflags;
+       uint            delblks, blkflags, prjflags = 0;
        xfs_dquot_t     *unresudq, *unresgdq, *delblksudq, *delblksgdq;
 
        ASSERT(XFS_ISLOCKED_INODE(ip));
@@ -2650,10 +2650,13 @@ xfs_qm_vop_chown_reserve(
                }
        }
        if (XFS_IS_OQUOTA_ON(ip->i_mount) && gdqp) {
-               if ((XFS_IS_GQUOTA_ON(ip->i_mount) &&
-                    ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id)) ||
-                   (XFS_IS_PQUOTA_ON(ip->i_mount) &&
-                    ip->i_d.di_projid != be32_to_cpu(gdqp->q_core.d_id))) {
+               if (XFS_IS_PQUOTA_ON(ip->i_mount) &&
+                    ip->i_d.di_projid != be32_to_cpu(gdqp->q_core.d_id))
+                       prjflags = XFS_QMOPT_ENOSPC;
+
+               if (prjflags ||
+                   (XFS_IS_GQUOTA_ON(ip->i_mount) &&
+                    ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id))) {
                        delblksgdq = gdqp;
                        if (delblks) {
                                ASSERT(ip->i_gdquot);
@@ -2664,7 +2667,7 @@ xfs_qm_vop_chown_reserve(
 
        if ((error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
                                delblksudq, delblksgdq, ip->i_d.di_nblocks, 1,
-                               flags | blkflags)))
+                               flags | blkflags | prjflags)))
                return (error);
 
        /*
@@ -2681,7 +2684,7 @@ xfs_qm_vop_chown_reserve(
                ASSERT(unresudq || unresgdq);
                if ((error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
                                delblksudq, delblksgdq, (xfs_qcnt_t)delblks, 0,
-                               flags | blkflags)))
+                               flags | blkflags | prjflags)))
                        return (error);
                xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
                                unresudq, unresgdq, -((xfs_qcnt_t)delblks), 0,
index d8e131e..9168918 100644 (file)
@@ -595,12 +595,19 @@ xfs_trans_unreserve_and_mod_dquots(
        }
 }
 
+STATIC int
+xfs_quota_error(uint flags)
+{
+       if (flags & XFS_QMOPT_ENOSPC)
+               return ENOSPC;
+       return EDQUOT;
+}
+
 /*
  * This reserves disk blocks and inodes against a dquot.
  * Flags indicate if the dquot is to be locked here and also
  * if the blk reservation is for RT or regular blocks.
  * Sending in XFS_QMOPT_FORCE_RES flag skips the quota check.
- * Returns EDQUOT if quota is exceeded.
  */
 STATIC int
 xfs_trans_dqresv(
@@ -666,19 +673,15 @@ xfs_trans_dqresv(
                         */
                        if (hardlimit > 0ULL &&
                             (hardlimit <= nblks + *resbcountp)) {
-                               error = EDQUOT;
+                               error = xfs_quota_error(flags);
                                goto error_return;
                        }
 
                        if (softlimit > 0ULL &&
                             (softlimit <= nblks + *resbcountp)) {
-                               /*
-                                * If timer or warnings has expired,
-                                * return EDQUOT
-                                */
                                if ((timer != 0 && get_seconds() > timer) ||
                                    (warns != 0 && warns >= warnlimit)) {
-                                       error = EDQUOT;
+                                       error = xfs_quota_error(flags);
                                        goto error_return;
                                }
                        }
@@ -695,16 +698,12 @@ xfs_trans_dqresv(
                        if (!softlimit)
                                softlimit = q->qi_isoftlimit;
                        if (hardlimit > 0ULL && count >= hardlimit) {
-                               error = EDQUOT;
+                               error = xfs_quota_error(flags);
                                goto error_return;
                        } else if (softlimit > 0ULL && count >= softlimit) {
-                               /*
-                                * If timer or warnings has expired,
-                                * return EDQUOT
-                                */
                                if ((timer != 0 && get_seconds() > timer) ||
                                     (warns != 0 && warns >= warnlimit)) {
-                                       error = EDQUOT;
+                                       error = xfs_quota_error(flags);
                                        goto error_return;
                                }
                        }
@@ -751,13 +750,14 @@ error_return:
 
 
 /*
- * Given dquot(s), make disk block and/or inode reservations against them.
+ * Given dquot(s), make disk block and/or inode reservations against them.
  * The fact that this does the reservation against both the usr and
- * grp quotas is important, because this follows a both-or-nothing
+ * grp/prj quotas is important, because this follows a both-or-nothing
  * approach.
  *
  * flags = XFS_QMOPT_DQLOCK indicate if dquot(s) need to be locked.
  *        XFS_QMOPT_FORCE_RES evades limit enforcement. Used by chown.
+ *        XFS_QMOPT_ENOSPC returns ENOSPC not EDQUOT.  Used by pquota.
  *        XFS_TRANS_DQ_RES_BLKS reserves regular disk blocks
  *        XFS_TRANS_DQ_RES_RTBLKS reserves realtime disk blocks
  * dquots are unlocked on return, if they were not locked by caller.
@@ -772,25 +772,27 @@ xfs_trans_reserve_quota_bydquots(
        long            ninos,
        uint            flags)
 {
-       int             resvd;
+       int             resvd = 0, error;
 
-       if (! XFS_IS_QUOTA_ON(mp))
-               return (0);
+       if (!XFS_IS_QUOTA_ON(mp))
+               return 0;
 
        if (tp && tp->t_dqinfo == NULL)
                xfs_trans_alloc_dqinfo(tp);
 
        ASSERT(flags & XFS_QMOPT_RESBLK_MASK);
-       resvd = 0;
 
        if (udqp) {
-               if (xfs_trans_dqresv(tp, mp, udqp, nblks, ninos, flags))
-                       return (EDQUOT);
+               error = xfs_trans_dqresv(tp, mp, udqp, nblks, ninos,
+                                       (flags & ~XFS_QMOPT_ENOSPC));
+               if (error)
+                       return error;
                resvd = 1;
        }
 
        if (gdqp) {
-               if (xfs_trans_dqresv(tp, mp, gdqp, nblks, ninos, flags)) {
+               error = xfs_trans_dqresv(tp, mp, gdqp, nblks, ninos, flags);
+               if (error) {
                        /*
                         * can't do it, so backout previous reservation
                         */
@@ -799,14 +801,14 @@ xfs_trans_reserve_quota_bydquots(
                                xfs_trans_dqresv(tp, mp, udqp,
                                                 -nblks, -ninos, flags);
                        }
-                       return (EDQUOT);
+                       return error;
                }
        }
 
        /*
         * Didn't change anything critical, so, no need to log
         */
-       return (0);
+       return 0;
 }
 
 
@@ -814,8 +816,6 @@ xfs_trans_reserve_quota_bydquots(
  * Lock the dquot and change the reservation if we can.
  * This doesn't change the actual usage, just the reservation.
  * The inode sent in is locked.
- *
- * Returns 0 on success, EDQUOT or other errors otherwise
  */
 STATIC int
 xfs_trans_reserve_quota_nblks(
@@ -824,20 +824,24 @@ xfs_trans_reserve_quota_nblks(
        xfs_inode_t     *ip,
        long            nblks,
        long            ninos,
-       uint            type)
+       uint            flags)
 {
        int             error;
 
        if (!XFS_IS_QUOTA_ON(mp))
-               return (0);
+               return 0;
+       if (XFS_IS_PQUOTA_ON(mp))
+               flags |= XFS_QMOPT_ENOSPC;
 
        ASSERT(ip->i_ino != mp->m_sb.sb_uquotino);
        ASSERT(ip->i_ino != mp->m_sb.sb_gquotino);
 
        ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));
        ASSERT(XFS_IS_QUOTA_RUNNING(ip->i_mount));
-       ASSERT((type & ~XFS_QMOPT_FORCE_RES) == XFS_TRANS_DQ_RES_RTBLKS ||
-              (type & ~XFS_QMOPT_FORCE_RES) == XFS_TRANS_DQ_RES_BLKS);
+       ASSERT((flags & ~(XFS_QMOPT_FORCE_RES | XFS_QMOPT_ENOSPC)) ==
+                               XFS_TRANS_DQ_RES_RTBLKS ||
+              (flags & ~(XFS_QMOPT_FORCE_RES | XFS_QMOPT_ENOSPC)) ==
+                               XFS_TRANS_DQ_RES_BLKS);
 
        /*
         * Reserve nblks against these dquots, with trans as the mediator.
@@ -845,8 +849,8 @@ xfs_trans_reserve_quota_nblks(
        error = xfs_trans_reserve_quota_bydquots(tp, mp,
                                                 ip->i_udquot, ip->i_gdquot,
                                                 nblks, ninos,
-                                                type);
-       return (error);
+                                                flags);
+       return error;
 }
 
 /*
index d384e48..26939d3 100644 (file)
@@ -4719,18 +4719,17 @@ xfs_bmapi(
                                /*
                                 * Make a transaction-less quota reservation for
                                 * delayed allocation blocks. This number gets
-                                * adjusted later.
-                                * We return EDQUOT if we haven't allocated
-                                * blks already inside this loop;
+                                * adjusted later.  We return if we haven't
+                                * allocated blocks already inside this loop.
                                 */
-                               if (XFS_TRANS_RESERVE_QUOTA_NBLKS(
+                               if ((error = XFS_TRANS_RESERVE_QUOTA_NBLKS(
                                                mp, NULL, ip, (long)alen, 0,
                                                rt ? XFS_QMOPT_RES_RTBLKS :
-                                                    XFS_QMOPT_RES_REGBLKS)) {
+                                                    XFS_QMOPT_RES_REGBLKS))) {
                                        if (n == 0) {
                                                *nmap = 0;
                                                ASSERT(cur == NULL);
-                                               return XFS_ERROR(EDQUOT);
+                                               return error;
                                        }
                                        break;
                                }
index f83399c..8e0d73d 100644 (file)
@@ -353,10 +353,11 @@ xfs_check_nostate_extents(
        xfs_extnum_t            num);
 
 /*
- * Call xfs_bmap_do_search_extents() to search for the extent
- * record containing block bno. If in multi-level in-core extent
- * allocation mode, find and extract the target extent buffer,
- * otherwise just use the direct extent list.
+ * Search the extent records for the entry containing block bno.
+ * If bno lies in a hole, point to the next entry.  If bno lies
+ * past eof, *eofp will be set, and *prevp will contain the last
+ * entry (null if none).  Else, *lastxp will be set to the index
+ * of the found entry; *gotp will contain the entry.
  */
 xfs_bmbt_rec_t *
 xfs_bmap_search_multi_extents(struct xfs_ifork *, xfs_fileoff_t, int *,
index 022fff6..5b7eb81 100644 (file)
@@ -68,6 +68,7 @@ struct xfs_mount_args {
                                                 * enforcement */
 #define XFSMNT_PQUOTAENF       0x00000040      /* IRIX project quota limit
                                                 * enforcement */
+#define XFSMNT_QUIET           0x00000080      /* don't report mount errors */
 #define XFSMNT_NOALIGN         0x00000200      /* don't allocate at
                                                 * stripe boundaries*/
 #define XFSMNT_RETERR          0x00000400      /* return error to user */
index 26b8e70..bc43163 100644 (file)
@@ -186,4 +186,7 @@ extern void xfs_fs_cmn_err(int level, struct xfs_mount *mp, char *fmt, ...);
 #define xfs_fs_repair_cmn_err(level, mp, fmt, args...) \
        xfs_fs_cmn_err(level, mp, fmt "  Unmount and run xfs_repair.", ## args)
 
+#define xfs_fs_mount_cmn_err(f, fmt, args...) \
+       ((f & XFS_MFSI_QUIET)? cmn_err(CE_WARN, "XFS: " fmt, ## args) : (void)0)
+
 #endif /* __XFS_ERROR_H__ */
index 72e7e78..049fabb 100644 (file)
@@ -213,7 +213,8 @@ xfs_mount_free(
 STATIC int
 xfs_mount_validate_sb(
        xfs_mount_t     *mp,
-       xfs_sb_t        *sbp)
+       xfs_sb_t        *sbp,
+       int             flags)
 {
        /*
         * If the log device and data device have the
@@ -223,33 +224,29 @@ xfs_mount_validate_sb(
         * a volume filesystem in a non-volume manner.
         */
        if (sbp->sb_magicnum != XFS_SB_MAGIC) {
-               cmn_err(CE_WARN, "XFS: bad magic number");
+               xfs_fs_mount_cmn_err(flags, "bad magic number");
                return XFS_ERROR(EWRONGFS);
        }
 
        if (!XFS_SB_GOOD_VERSION(sbp)) {
-               cmn_err(CE_WARN, "XFS: bad version");
+               xfs_fs_mount_cmn_err(flags, "bad version");
                return XFS_ERROR(EWRONGFS);
        }
 
        if (unlikely(
            sbp->sb_logstart == 0 && mp->m_logdev_targp == mp->m_ddev_targp)) {
-               cmn_err(CE_WARN,
-       "XFS: filesystem is marked as having an external log; "
-       "specify logdev on the\nmount command line.");
-               XFS_CORRUPTION_ERROR("xfs_mount_validate_sb(1)",
-                                    XFS_ERRLEVEL_HIGH, mp, sbp);
-               return XFS_ERROR(EFSCORRUPTED);
+               xfs_fs_mount_cmn_err(flags,
+                       "filesystem is marked as having an external log; "
+                       "specify logdev on the\nmount command line.");
+               return XFS_ERROR(EINVAL);
        }
 
        if (unlikely(
            sbp->sb_logstart != 0 && mp->m_logdev_targp != mp->m_ddev_targp)) {
-               cmn_err(CE_WARN,
-       "XFS: filesystem is marked as having an internal log; "
-       "don't specify logdev on\nthe mount command line.");
-               XFS_CORRUPTION_ERROR("xfs_mount_validate_sb(2)",
-                                    XFS_ERRLEVEL_HIGH, mp, sbp);
-               return XFS_ERROR(EFSCORRUPTED);
+               xfs_fs_mount_cmn_err(flags,
+                       "filesystem is marked as having an internal log; "
+                       "do not specify logdev on\nthe mount command line.");
+               return XFS_ERROR(EINVAL);
        }
 
        /*
@@ -274,9 +271,7 @@ xfs_mount_validate_sb(
            (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE)  ||
            (sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE)  ||
            (sbp->sb_imax_pct > 100 || sbp->sb_imax_pct < 1))) {
-               cmn_err(CE_WARN, "XFS: SB sanity check 1 failed");
-               XFS_CORRUPTION_ERROR("xfs_mount_validate_sb(3)",
-                                    XFS_ERRLEVEL_LOW, mp, sbp);
+               xfs_fs_mount_cmn_err(flags, "SB sanity check 1 failed");
                return XFS_ERROR(EFSCORRUPTED);
        }
 
@@ -289,9 +284,7 @@ xfs_mount_validate_sb(
             (xfs_drfsbno_t)sbp->sb_agcount * sbp->sb_agblocks ||
            sbp->sb_dblocks < (xfs_drfsbno_t)(sbp->sb_agcount - 1) *
                              sbp->sb_agblocks + XFS_MIN_AG_BLOCKS)) {
-               cmn_err(CE_WARN, "XFS: SB sanity check 2 failed");
-               XFS_ERROR_REPORT("xfs_mount_validate_sb(4)",
-                                XFS_ERRLEVEL_LOW, mp);
+               xfs_fs_mount_cmn_err(flags, "SB sanity check 2 failed");
                return XFS_ERROR(EFSCORRUPTED);
        }
 
@@ -307,15 +300,13 @@ xfs_mount_validate_sb(
            (sbp->sb_dblocks << (sbp->sb_blocklog - BBSHIFT)) > UINT_MAX ||
            (sbp->sb_rblocks << (sbp->sb_blocklog - BBSHIFT)) > UINT_MAX)) {
 #endif
-               cmn_err(CE_WARN,
-       "XFS: File system is too large to be mounted on this system.");
+               xfs_fs_mount_cmn_err(flags,
+                       "file system too large to be mounted on this system.");
                return XFS_ERROR(E2BIG);
        }
 
        if (unlikely(sbp->sb_inprogress)) {
-               cmn_err(CE_WARN, "XFS: file system busy");
-               XFS_ERROR_REPORT("xfs_mount_validate_sb(5)",
-                                XFS_ERRLEVEL_LOW, mp);
+               xfs_fs_mount_cmn_err(flags, "file system busy");
                return XFS_ERROR(EFSCORRUPTED);
        }
 
@@ -323,8 +314,8 @@ xfs_mount_validate_sb(
         * Version 1 directory format has never worked on Linux.
         */
        if (unlikely(!XFS_SB_VERSION_HASDIRV2(sbp))) {
-               cmn_err(CE_WARN,
-       "XFS: Attempted to mount file system using version 1 directory format");
+               xfs_fs_mount_cmn_err(flags,
+                       "file system using version 1 directory format");
                return XFS_ERROR(ENOSYS);
        }
 
@@ -332,11 +323,11 @@ xfs_mount_validate_sb(
         * Until this is fixed only page-sized or smaller data blocks work.
         */
        if (unlikely(sbp->sb_blocksize > PAGE_SIZE)) {
-               cmn_err(CE_WARN,
-               "XFS: Attempted to mount file system with blocksize %d bytes",
+               xfs_fs_mount_cmn_err(flags,
+                       "file system with blocksize %d bytes",
                        sbp->sb_blocksize);
-               cmn_err(CE_WARN,
-               "XFS: Only page-sized (%ld) or less blocksizes currently work.",
+               xfs_fs_mount_cmn_err(flags,
+                       "only pagesize (%ld) or less will currently work.",
                        PAGE_SIZE);
                return XFS_ERROR(ENOSYS);
        }
@@ -484,7 +475,7 @@ xfs_xlatesb(
  * Does the initial read of the superblock.
  */
 int
-xfs_readsb(xfs_mount_t *mp)
+xfs_readsb(xfs_mount_t *mp, int flags)
 {
        unsigned int    sector_size;
        unsigned int    extra_flags;
@@ -506,7 +497,7 @@ xfs_readsb(xfs_mount_t *mp)
        bp = xfs_buf_read_flags(mp->m_ddev_targp, XFS_SB_DADDR,
                                BTOBB(sector_size), extra_flags);
        if (!bp || XFS_BUF_ISERROR(bp)) {
-               cmn_err(CE_WARN, "XFS: SB read failed");
+               xfs_fs_mount_cmn_err(flags, "SB read failed");
                error = bp ? XFS_BUF_GETERROR(bp) : ENOMEM;
                goto fail;
        }
@@ -520,9 +511,9 @@ xfs_readsb(xfs_mount_t *mp)
        sbp = XFS_BUF_TO_SBP(bp);
        xfs_xlatesb(XFS_BUF_PTR(bp), &(mp->m_sb), 1, XFS_SB_ALL_BITS);
 
-       error = xfs_mount_validate_sb(mp, &(mp->m_sb));
+       error = xfs_mount_validate_sb(mp, &(mp->m_sb), flags);
        if (error) {
-               cmn_err(CE_WARN, "XFS: SB validate failed");
+               xfs_fs_mount_cmn_err(flags, "SB validate failed");
                goto fail;
        }
 
@@ -530,8 +521,8 @@ xfs_readsb(xfs_mount_t *mp)
         * We must be able to do sector-sized and sector-aligned IO.
         */
        if (sector_size > mp->m_sb.sb_sectsize) {
-               cmn_err(CE_WARN,
-                       "XFS: device supports only %u byte sectors (not %u)",
+               xfs_fs_mount_cmn_err(flags,
+                       "device supports only %u byte sectors (not %u)",
                        sector_size, mp->m_sb.sb_sectsize);
                error = ENOSYS;
                goto fail;
@@ -548,7 +539,7 @@ xfs_readsb(xfs_mount_t *mp)
                bp = xfs_buf_read_flags(mp->m_ddev_targp, XFS_SB_DADDR,
                                        BTOBB(sector_size), extra_flags);
                if (!bp || XFS_BUF_ISERROR(bp)) {
-                       cmn_err(CE_WARN, "XFS: SB re-read failed");
+                       xfs_fs_mount_cmn_err(flags, "SB re-read failed");
                        error = bp ? XFS_BUF_GETERROR(bp) : ENOMEM;
                        goto fail;
                }
@@ -678,7 +669,7 @@ xfs_mountfs(
        int             error = 0;
 
        if (mp->m_sb_bp == NULL) {
-               if ((error = xfs_readsb(mp))) {
+               if ((error = xfs_readsb(mp, mfsi_flags))) {
                        return error;
                }
        }
index 66cbee7..668ad23 100644 (file)
@@ -510,9 +510,12 @@ xfs_preferred_iosize(xfs_mount_t *mp)
  */
 #define XFS_MFSI_SECOND                0x01    /* Secondary mount -- skip stuff */
 #define XFS_MFSI_CLIENT                0x02    /* Is a client -- skip lots of stuff */
+/*     XFS_MFSI_RRINODES       */
 #define XFS_MFSI_NOUNLINK      0x08    /* Skip unlinked inode processing in */
                                        /* log recovery */
 #define XFS_MFSI_NO_QUOTACHECK 0x10    /* Skip quotacheck processing */
+/*     XFS_MFSI_CONVERT_SUNIT  */
+#define XFS_MFSI_QUIET         0x40    /* Be silent if mount errors found */
 
 /*
  * Macros for getting from mount to vfs and back.
@@ -581,7 +584,7 @@ extern int  xfs_mod_incore_sb_unlocked(xfs_mount_t *, xfs_sb_field_t,
 extern int     xfs_mod_incore_sb_batch(xfs_mount_t *, xfs_mod_sb_t *,
                        uint, int);
 extern struct xfs_buf *xfs_getsb(xfs_mount_t *, int);
-extern int     xfs_readsb(xfs_mount_t *mp);
+extern int     xfs_readsb(xfs_mount_t *, int);
 extern void    xfs_freesb(xfs_mount_t *);
 extern void    xfs_do_force_shutdown(bhv_desc_t *, int, char *, int);
 extern int     xfs_syncsub(xfs_mount_t *, int, int, int *);
index 4f6a034..7fbef97 100644 (file)
@@ -196,10 +196,11 @@ typedef struct xfs_qoff_logformat {
 #define XFS_QMOPT_QUOTAOFF     0x0000080 /* quotas are being turned off */
 #define XFS_QMOPT_UMOUNTING    0x0000100 /* filesys is being unmounted */
 #define XFS_QMOPT_DOLOG                0x0000200 /* log buf changes (in quotacheck) */
-#define XFS_QMOPT_DOWARN        0x0000400 /* increase warning cnt if necessary */
+#define XFS_QMOPT_DOWARN        0x0000400 /* increase warning cnt if needed */
 #define XFS_QMOPT_ILOCKED      0x0000800 /* inode is already locked (excl) */
-#define XFS_QMOPT_DQREPAIR     0x0001000 /* repair dquot, if damaged. */
+#define XFS_QMOPT_DQREPAIR     0x0001000 /* repair dquot if damaged */
 #define XFS_QMOPT_GQUOTA       0x0002000 /* group dquot requested */
+#define XFS_QMOPT_ENOSPC       0x0004000 /* enospc instead of edquot (prj) */
 
 /*
  * flags to xfs_trans_mod_dquot to indicate which field needs to be
index 504d2a8..f0e09ca 100644 (file)
@@ -442,6 +442,9 @@ xfs_mount(
        p = vfs_bhv_lookup(vfsp, VFS_POSITION_IO);
        mp->m_io_ops = p ? *(xfs_ioops_t *) vfs_bhv_custom(p) : xfs_iocore_xfs;
 
+       if (args->flags & XFSMNT_QUIET)
+               flags |= XFS_MFSI_QUIET;
+
        /*
         * Open real time and log devices - order is important.
         */
@@ -492,7 +495,7 @@ xfs_mount(
        error = xfs_start_flags(vfsp, args, mp);
        if (error)
                goto error1;
-       error = xfs_readsb(mp);
+       error = xfs_readsb(mp, flags);
        if (error)
                goto error1;
        error = xfs_finish_flags(vfsp, args, mp);
@@ -1697,8 +1700,9 @@ xfs_parseargs(
        int                     dsunit, dswidth, vol_dsunit, vol_dswidth;
        int                     iosize;
 
-       args->flags2 |= XFSMNT2_COMPAT_IOSIZE;
        args->flags |= XFSMNT_IDELETE;
+       args->flags |= XFSMNT_BARRIER;
+       args->flags2 |= XFSMNT2_COMPAT_IOSIZE;
 
        if (!options)
                goto done;
@@ -1947,8 +1951,6 @@ xfs_showargs(
                seq_printf(m, "," MNTOPT_IKEEP);
        if (!(mp->m_flags & XFS_MOUNT_COMPAT_IOSIZE))
                seq_printf(m, "," MNTOPT_LARGEIO);
-       if (mp->m_flags & XFS_MOUNT_BARRIER)
-               seq_printf(m, "," MNTOPT_BARRIER);
 
        if (!(vfsp->vfs_flag & VFS_32BITINODES))
                seq_printf(m, "," MNTOPT_64BITINODE);
index de49601..fa71b30 100644 (file)
@@ -4648,6 +4648,10 @@ vnodeops_t xfs_vnodeops = {
        .vop_read               = xfs_read,
 #ifdef HAVE_SENDFILE
        .vop_sendfile           = xfs_sendfile,
+#endif
+#ifdef HAVE_SPLICE
+       .vop_splice_read        = xfs_splice_read,
+       .vop_splice_write       = xfs_splice_write,
 #endif
        .vop_write              = xfs_write,
        .vop_ioctl              = xfs_ioctl,
index 2646c01..59e6f44 100644 (file)
@@ -65,6 +65,9 @@
 /* SmartMedia */
 #define AT91_SMARTMEDIA_BASE   0x40000000      /* NCS3: Smartmedia physical base address */
 
+/* Compact Flash */
+#define AT91_CF_BASE           0x50000000      /* NCS4-NCS6: Compact Flash physical base address */
+
 /* Multi-Master Memory controller */
 #define AT91_UHP_BASE          0x00300000      /* USB Host controller */
 
index 5207758..868c713 100644 (file)
@@ -60,23 +60,9 @@ void __init pci_addr_cache_build(void);
  * device (including config space i/o).  Call eeh_add_device_late
  * to finish the eeh setup for this device.
  */
-void eeh_add_device_early(struct device_node *);
-void eeh_add_device_late(struct pci_dev *dev);
 void eeh_add_device_tree_early(struct device_node *);
 void eeh_add_device_tree_late(struct pci_bus *);
 
-/**
- * eeh_remove_device - undo EEH setup for the indicated pci device
- * @dev: pci device to be removed
- *
- * This routine should be called when a device is removed from
- * a running system (e.g. by hotplug or dlpar).  It unregisters
- * the PCI device from the EEH subsystem.  I/O errors affecting
- * this device will no longer be detected after this call; thus,
- * i/o errors affecting this slot may leave this device unusable.
- */
-void eeh_remove_device(struct pci_dev *);
-
 /**
  * eeh_remove_device_recursive - undo EEH for device & children.
  * @dev: pci device to be removed
@@ -116,12 +102,6 @@ static inline int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *d
 
 static inline void pci_addr_cache_build(void) { }
 
-static inline void eeh_add_device_early(struct device_node *dn) { }
-
-static inline void eeh_add_device_late(struct pci_dev *dev) { }
-
-static inline void eeh_remove_device(struct pci_dev *dev) { }
-
 static inline void eeh_add_device_tree_early(struct device_node *dn) { }
 
 static inline void eeh_add_device_tree_late(struct pci_bus *bus) { }
index b72c04f..6cc7e1f 100644 (file)
@@ -4,47 +4,88 @@
 
 #define HVSC                   .long 0x44000022
 
-#define H_Success      0
-#define H_Busy         1       /* Hardware busy -- retry later */
-#define H_Closed       2       /* Resource closed */
-#define H_Constrained  4       /* Resource request constrained to max allowed */
-#define H_InProgress   14      /* Kind of like busy */
-#define H_Pending      17      /* returned from H_POLL_PENDING */
-#define H_Continue     18      /* Returned from H_Join on success */
-#define H_LongBusyStartRange   9900  /* Start of long busy range */
-#define H_LongBusyOrder1msec   9900  /* Long busy, hint that 1msec is a good time to retry */
-#define H_LongBusyOrder10msec  9901  /* Long busy, hint that 10msec is a good time to retry */
-#define H_LongBusyOrder100msec 9902  /* Long busy, hint that 100msec is a good time to retry */
-#define H_LongBusyOrder1sec    9903  /* Long busy, hint that 1sec is a good time to retry */
-#define H_LongBusyOrder10sec   9904  /* Long busy, hint that 10sec is a good time to retry */
-#define H_LongBusyOrder100sec  9905  /* Long busy, hint that 100sec is a good time to retry */
-#define H_LongBusyEndRange     9905  /* End of long busy range */
-#define H_Hardware     -1      /* Hardware error */
-#define H_Function     -2      /* Function not supported */
-#define H_Privilege    -3      /* Caller not privileged */
-#define H_Parameter    -4      /* Parameter invalid, out-of-range or conflicting */
-#define H_Bad_Mode     -5      /* Illegal msr value */
-#define H_PTEG_Full    -6      /* PTEG is full */
-#define H_Not_Found    -7      /* PTE was not found" */
-#define H_Reserved_DABR        -8      /* DABR address is reserved by the hypervisor on this processor" */
-#define H_NoMem                 -9
-#define H_Authority            -10
-#define H_Permission           -11
-#define H_Dropped              -12
-#define H_SourceParm           -13
-#define H_DestParm             -14
-#define H_RemoteParm           -15
-#define H_Resource             -16
+#define H_SUCCESS      0
+#define H_BUSY         1       /* Hardware busy -- retry later */
+#define H_CLOSED       2       /* Resource closed */
+#define H_NOT_AVAILABLE 3
+#define H_CONSTRAINED  4       /* Resource request constrained to max allowed */
+#define H_PARTIAL       5
+#define H_IN_PROGRESS  14      /* Kind of like busy */
+#define H_PAGE_REGISTERED 15
+#define H_PARTIAL_STORE   16
+#define H_PENDING      17      /* returned from H_POLL_PENDING */
+#define H_CONTINUE     18      /* Returned from H_Join on success */
+#define H_LONG_BUSY_START_RANGE                9900  /* Start of long busy range */
+#define H_LONG_BUSY_ORDER_1_MSEC       9900  /* Long busy, hint that 1msec \
+                                                is a good time to retry */
+#define H_LONG_BUSY_ORDER_10_MSEC      9901  /* Long busy, hint that 10msec \
+                                                is a good time to retry */
+#define H_LONG_BUSY_ORDER_100_MSEC     9902  /* Long busy, hint that 100msec \
+                                                is a good time to retry */
+#define H_LONG_BUSY_ORDER_1_SEC                9903  /* Long busy, hint that 1sec \
+                                                is a good time to retry */
+#define H_LONG_BUSY_ORDER_10_SEC       9904  /* Long busy, hint that 10sec \
+                                                is a good time to retry */
+#define H_LONG_BUSY_ORDER_100_SEC      9905  /* Long busy, hint that 100sec \
+                                                is a good time to retry */
+#define H_LONG_BUSY_END_RANGE          9905  /* End of long busy range */
+#define H_HARDWARE     -1      /* Hardware error */
+#define H_FUNCTION     -2      /* Function not supported */
+#define H_PRIVILEGE    -3      /* Caller not privileged */
+#define H_PARAMETER    -4      /* Parameter invalid, out-of-range or conflicting */
+#define H_BAD_MODE     -5      /* Illegal msr value */
+#define H_PTEG_FULL    -6      /* PTEG is full */
+#define H_NOT_FOUND    -7      /* PTE was not found" */
+#define H_RESERVED_DABR        -8      /* DABR address is reserved by the hypervisor on this processor" */
+#define H_NO_MEM       -9
+#define H_AUTHORITY    -10
+#define H_PERMISSION   -11
+#define H_DROPPED      -12
+#define H_SOURCE_PARM  -13
+#define H_DEST_PARM    -14
+#define H_REMOTE_PARM  -15
+#define H_RESOURCE     -16
+#define H_ADAPTER_PARM  -17
+#define H_RH_PARM       -18
+#define H_RCQ_PARM      -19
+#define H_SCQ_PARM      -20
+#define H_EQ_PARM       -21
+#define H_RT_PARM       -22
+#define H_ST_PARM       -23
+#define H_SIGT_PARM     -24
+#define H_TOKEN_PARM    -25
+#define H_MLENGTH_PARM  -27
+#define H_MEM_PARM      -28
+#define H_MEM_ACCESS_PARM -29
+#define H_ATTR_PARM     -30
+#define H_PORT_PARM     -31
+#define H_MCG_PARM      -32
+#define H_VL_PARM       -33
+#define H_TSIZE_PARM    -34
+#define H_TRACE_PARM    -35
+
+#define H_MASK_PARM     -37
+#define H_MCG_FULL      -38
+#define H_ALIAS_EXIST   -39
+#define H_P_COUNTER     -40
+#define H_TABLE_FULL    -41
+#define H_ALT_TABLE     -42
+#define H_MR_CONDITION  -43
+#define H_NOT_ENOUGH_RESOURCES -44
+#define H_R_STATE       -45
+#define H_RESCINDEND    -46
+
 
 /* Long Busy is a condition that can be returned by the firmware
  * when a call cannot be completed now, but the identical call
  * should be retried later.  This prevents calls blocking in the
- * firmware for long periods of time. Annoyingly the firmware can return
+ * firmware for long periods of time.  Annoyingly the firmware can return
  * a range of return codes, hinting at how long we should wait before
  * retrying.  If you don't care for the hint, the macro below is a good
  * way to check for the long_busy return codes
  */
-#define H_isLongBusy(x)  ((x >= H_LongBusyStartRange) && (x <= H_LongBusyEndRange))
+#define H_IS_LONG_BUSY(x)  ((x >= H_LONG_BUSY_START_RANGE) \
+                            && (x <= H_LONG_BUSY_END_RANGE))
 
 /* Flags */
 #define H_LARGE_PAGE           (1UL<<(63-16))
 #define H_DABRX_KERNEL         (1UL<<(63-62))
 #define H_DABRX_USER           (1UL<<(63-63))
 
+/* Each control block has to be on a 4K bondary */
+#define H_CB_ALIGNMENT          4096
+
 /* pSeries hypervisor opcodes */
 #define H_REMOVE               0x04
 #define H_ENTER                        0x08
 #define H_PERFMON              0x7c
 #define H_MIGRATE_DMA          0x78
 #define H_REGISTER_VPA         0xDC
-#define H_CEDE                 0xE0
+#define H_CEDE                 0xE0
 #define H_CONFER               0xE4
-#define H_PROD                 0xE8
+#define H_PROD                 0xE8
 #define H_GET_PPP              0xEC
 #define H_SET_PPP              0xF0
 #define H_PURR                 0xF4
-#define H_PIC                  0xF8
+#define H_PIC                  0xF8
 #define H_REG_CRQ              0xFC
 #define H_FREE_CRQ             0x100
 #define H_VIO_SIGNAL           0x104
 #define H_SEND_CRQ             0x108
-#define H_COPY_RDMA             0x110
+#define H_COPY_RDMA            0x110
 #define H_SET_XDABR            0x134
 #define H_STUFF_TCE            0x138
 #define H_PUT_TCE_INDIRECT     0x13C
 #define H_VTERM_PARTNER_INFO   0x150
 #define H_REGISTER_VTERM       0x154
 #define H_FREE_VTERM           0x158
-#define H_POLL_PENDING         0x1D8
+#define H_RESET_EVENTS          0x15C
+#define H_ALLOC_RESOURCE        0x160
+#define H_FREE_RESOURCE         0x164
+#define H_MODIFY_QP             0x168
+#define H_QUERY_QP              0x16C
+#define H_REREGISTER_PMR        0x170
+#define H_REGISTER_SMR          0x174
+#define H_QUERY_MR              0x178
+#define H_QUERY_MW              0x17C
+#define H_QUERY_HCA             0x180
+#define H_QUERY_PORT            0x184
+#define H_MODIFY_PORT           0x188
+#define H_DEFINE_AQP1           0x18C
+#define H_GET_TRACE_BUFFER      0x190
+#define H_DEFINE_AQP0           0x194
+#define H_RESIZE_MR             0x198
+#define H_ATTACH_MCQP           0x19C
+#define H_DETACH_MCQP           0x1A0
+#define H_CREATE_RPT            0x1A4
+#define H_REMOVE_RPT            0x1A8
+#define H_REGISTER_RPAGES       0x1AC
+#define H_DISABLE_AND_GETC      0x1B0
+#define H_ERROR_DATA            0x1B4
+#define H_GET_HCA_INFO          0x1B8
+#define H_GET_PERF_COUNT        0x1BC
+#define H_MANAGE_TRACE          0x1C0
+#define H_QUERY_INT_STATE       0x1E4
+#define H_POLL_PENDING         0x1D8
 #define H_JOIN                 0x298
 #define H_ENABLE_CRQ           0x2B0
 
@@ -152,7 +223,7 @@ long plpar_hcall_norets(unsigned long opcode, ...);
  */
 long plpar_hcall_8arg_2ret(unsigned long opcode,
                           unsigned long arg1,
-                          unsigned long arg2,
+                          unsigned long arg2,
                           unsigned long arg3,
                           unsigned long arg4,
                           unsigned long arg5,
@@ -176,6 +247,42 @@ long plpar_hcall_4out(unsigned long opcode,
                      unsigned long *out3,
                      unsigned long *out4);
 
+long plpar_hcall_7arg_7ret(unsigned long opcode,
+                          unsigned long arg1,
+                          unsigned long arg2,
+                          unsigned long arg3,
+                          unsigned long arg4,
+                          unsigned long arg5,
+                          unsigned long arg6,
+                          unsigned long arg7,
+                          unsigned long *out1,
+                          unsigned long *out2,
+                          unsigned long *out3,
+                          unsigned long *out4,
+                          unsigned long *out5,
+                          unsigned long *out6,
+                          unsigned long *out7);
+
+long plpar_hcall_9arg_9ret(unsigned long opcode,
+                          unsigned long arg1,
+                          unsigned long arg2,
+                          unsigned long arg3,
+                          unsigned long arg4,
+                          unsigned long arg5,
+                          unsigned long arg6,
+                          unsigned long arg7,
+                          unsigned long arg8,
+                          unsigned long arg9,
+                          unsigned long *out1,
+                          unsigned long *out2,
+                          unsigned long *out3,
+                          unsigned long *out4,
+                          unsigned long *out5,
+                          unsigned long *out6,
+                          unsigned long *out7,
+                          unsigned long *out8,
+                          unsigned long *out9);
+
 #endif /* __ASSEMBLY__ */
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_HVCALL_H */
index 64ec640..264f0eb 100644 (file)
 #define __NR_sched_get_affinity 161 /* Linux specific, getfh under SunOS           */
 #define __NR_getdomainname      162 /* SunOS Specific                              */
 #define __NR_setdomainname      163 /* Common                                      */
-/* #define __NR_ni_syscall     164    ENOSYS under SunOS                          */
+/* #define __NR_utrap_install   164    Linux sparc64 specific                     */
 #define __NR_quotactl           165 /* Common                                      */
 #define __NR_set_tid_address    166 /* Linux specific, exportfs under SunOS        */
 #define __NR_mount              167 /* Common                                      */
 #define __NR_setfsgid           229 /* Linux Specific                              */
 #define __NR__newselect         230 /* Linux Specific                              */
 #define __NR_time               231 /* Linux Specific                              */
-/* #define __NR_oldstat         232    Linux Specific                              */
+#define __NR_sys_splice         232 /* Linux Specific                              */
 #define __NR_stime              233 /* Linux Specific                              */
 #define __NR_statfs64           234 /* Linux Specific                              */
 #define __NR_fstatfs64          235 /* Linux Specific                              */
 #define __NR_getsid             252
 #define __NR_fdatasync          253
 #define __NR_nfsservctl         254
-#define __NR_aplib              255
+#define __NR_sys_sync_file_range 255
 #define __NR_clock_settime     256
 #define __NR_clock_gettime     257
 #define __NR_clock_getres      258
index a284986..d0544b4 100644 (file)
 #ifdef __KERNEL__
 #define __NR_time              231 /* Linux sparc32                               */
 #endif
-/* #define __NR_oldstat         232    Linux Specific                              */
+#define __NR_sys_splice         232 /* Linux Specific                              */
 #define __NR_stime              233 /* Linux Specific                              */
 #define __NR_statfs64           234 /* Linux Specific                              */
 #define __NR_fstatfs64          235 /* Linux Specific                              */
 #define __NR_getsid             252
 #define __NR_fdatasync          253
 #define __NR_nfsservctl         254
-#define __NR_aplib              255
+#define __NR_sys_sync_file_range 255
 #define __NR_clock_settime     256
 #define __NR_clock_gettime     257
 #define __NR_clock_getres      258
index 4ed7e60..1e9ebab 100644 (file)
@@ -864,7 +864,7 @@ struct super_block {
         */
        struct mutex s_vfs_rename_mutex;        /* Kludge */
 
-       /* Granuality of c/m/atime in ns.
+       /* Granularity of c/m/atime in ns.
           Cannot be worse than a second */
        u32                s_time_gran;
 };
index 9c8e6da..71e7b28 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <asm/io.h>
 #include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/device.h>
 #include <linux/timer.h>
 
@@ -40,7 +41,7 @@ struct gameport {
        struct gameport *parent, *child;
 
        struct gameport_driver *drv;
-       struct semaphore drv_sem;       /* protects serio->drv so attributes can pin driver */
+       struct mutex drv_mutex;         /* protects serio->drv so attributes can pin driver */
 
        struct device dev;
        unsigned int registered;        /* port has been fully registered with driver core */
@@ -137,12 +138,12 @@ static inline void gameport_set_drvdata(struct gameport *gameport, void *data)
  */
 static inline int gameport_pin_driver(struct gameport *gameport)
 {
-       return down_interruptible(&gameport->drv_sem);
+       return mutex_lock_interruptible(&gameport->drv_mutex);
 }
 
 static inline void gameport_unpin_driver(struct gameport *gameport)
 {
-       up(&gameport->drv_sem);
+       mutex_unlock(&gameport->drv_mutex);
 }
 
 void __gameport_register_driver(struct gameport_driver *drv, struct module *owner);
index b209392..306acf1 100644 (file)
@@ -80,7 +80,7 @@ struct hrtimer_sleeper {
  * @first:             pointer to the timer node which expires first
  * @resolution:                the resolution of the clock, in nanoseconds
  * @get_time:          function to retrieve the current time of the clock
- * @get_sofirq_time:   function to retrieve the current time from the softirq
+ * @get_softirq_time:  function to retrieve the current time from the softirq
  * @curr_timer:                the timer which is executing a callback right now
  * @softirq_time:      the time when running the hrtimer queue in the softirq
  */
index 1d4e341..b0e612d 100644 (file)
@@ -421,7 +421,7 @@ struct input_absinfo {
 #define BTN_GEAR_UP            0x151
 
 #define KEY_OK                 0x160
-#define KEY_SELECT             0x161
+#define KEY_SELECT             0x161
 #define KEY_GOTO               0x162
 #define KEY_CLEAR              0x163
 #define KEY_POWER2             0x164
@@ -512,6 +512,15 @@ struct input_absinfo {
 #define KEY_FN_S               0x1e3
 #define KEY_FN_B               0x1e4
 
+#define KEY_BRL_DOT1           0x1f1
+#define KEY_BRL_DOT2           0x1f2
+#define KEY_BRL_DOT3           0x1f3
+#define KEY_BRL_DOT4           0x1f4
+#define KEY_BRL_DOT5           0x1f5
+#define KEY_BRL_DOT6           0x1f6
+#define KEY_BRL_DOT7           0x1f7
+#define KEY_BRL_DOT8           0x1f8
+
 /* We avoid low common keys in module aliases so they don't get huge. */
 #define KEY_MIN_INTERESTING    KEY_MUTE
 #define KEY_MAX                        0x1ff
@@ -929,7 +938,7 @@ struct input_dev {
 
        struct input_handle *grab;
 
-       struct semaphore sem;   /* serializes open and close operations */
+       struct mutex mutex;     /* serializes open and close operations */
        unsigned int users;
 
        struct class_device cdev;
@@ -995,11 +1004,6 @@ static inline void init_input_dev(struct input_dev *dev)
 
 struct input_dev *input_allocate_device(void);
 
-static inline void input_free_device(struct input_dev *dev)
-{
-       kfree(dev);
-}
-
 static inline struct input_dev *input_get_device(struct input_dev *dev)
 {
        return to_input_dev(class_device_get(&dev->cdev));
@@ -1010,6 +1014,11 @@ static inline void input_put_device(struct input_dev *dev)
        class_device_put(&dev->cdev);
 }
 
+static inline void input_free_device(struct input_dev *dev)
+{
+       input_put_device(dev);
+}
+
 int input_register_device(struct input_dev *);
 void input_unregister_device(struct input_dev *);
 
index e87c32a..4eb851e 100644 (file)
@@ -135,6 +135,8 @@ static inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
 
 #define U(x) ((x) ^ 0xf000)
 
+#define BRL_UC_ROW 0x2800
+
 /* keyboard.c */
 
 struct console;
index 0848804..de76843 100644 (file)
@@ -44,6 +44,7 @@ extern unsigned short plain_map[NR_KEYS];
 #define KT_ASCII       9
 #define KT_LOCK                10
 #define KT_SLOCK       12
+#define KT_BRL         14
 
 #define K(t,v)         (((t)<<8)|(v))
 #define KTYP(x)                ((x) >> 8)
@@ -427,5 +428,17 @@ extern unsigned short plain_map[NR_KEYS];
 
 #define NR_LOCK                8
 
+#define K_BRL_BLANK     K(KT_BRL, 0)
+#define K_BRL_DOT1      K(KT_BRL, 1)
+#define K_BRL_DOT2      K(KT_BRL, 2)
+#define K_BRL_DOT3      K(KT_BRL, 3)
+#define K_BRL_DOT4      K(KT_BRL, 4)
+#define K_BRL_DOT5      K(KT_BRL, 5)
+#define K_BRL_DOT6      K(KT_BRL, 6)
+#define K_BRL_DOT7      K(KT_BRL, 7)
+#define K_BRL_DOT8      K(KT_BRL, 8)
+
+#define NR_BRL         9
+
 #define MAX_DIACR      256
 #endif
index a710bdd..08a450a 100644 (file)
@@ -28,7 +28,7 @@ struct ps2dev {
        struct serio *serio;
 
        /* Ensures that only one command is executing at a time */
-       struct semaphore cmd_sem;
+       struct mutex cmd_mutex;
 
        /* Used to signal completion from interrupt handler */
        wait_queue_head_t wait;
index 1350e47..f6bdef8 100644 (file)
@@ -142,6 +142,12 @@ struct xt_counters_info
 #define ASSERT_WRITE_LOCK(x)
 #include <linux/netfilter_ipv4/listhelp.h>
 
+#ifdef CONFIG_COMPAT
+#define COMPAT_TO_USER         1
+#define COMPAT_FROM_USER       -1
+#define COMPAT_CALC_SIZE       0
+#endif
+
 struct xt_match
 {
        struct list_head list;
@@ -175,6 +181,9 @@ struct xt_match
        void (*destroy)(const struct xt_match *match, void *matchinfo,
                        unsigned int matchinfosize);
 
+       /* Called when userspace align differs from kernel space one */
+       int (*compat)(void *match, void **dstptr, int *size, int convert);
+
        /* Set this to THIS_MODULE if you are a module, otherwise NULL */
        struct module *me;
 
@@ -220,6 +229,9 @@ struct xt_target
        void (*destroy)(const struct xt_target *target, void *targinfo,
                        unsigned int targinfosize);
 
+       /* Called when userspace align differs from kernel space one */
+       int (*compat)(void *target, void **dstptr, int *size, int convert);
+
        /* Set this to THIS_MODULE if you are a module, otherwise NULL */
        struct module *me;
 
@@ -314,6 +326,61 @@ extern void xt_proto_fini(int af);
 extern struct xt_table_info *xt_alloc_table_info(unsigned int size);
 extern void xt_free_table_info(struct xt_table_info *info);
 
+#ifdef CONFIG_COMPAT
+#include <net/compat.h>
+
+struct compat_xt_entry_match
+{
+       union {
+               struct {
+                       u_int16_t match_size;
+                       char name[XT_FUNCTION_MAXNAMELEN - 1];
+                       u_int8_t revision;
+               } user;
+               u_int16_t match_size;
+       } u;
+       unsigned char data[0];
+};
+
+struct compat_xt_entry_target
+{
+       union {
+               struct {
+                       u_int16_t target_size;
+                       char name[XT_FUNCTION_MAXNAMELEN - 1];
+                       u_int8_t revision;
+               } user;
+               u_int16_t target_size;
+       } u;
+       unsigned char data[0];
+};
+
+/* FIXME: this works only on 32 bit tasks
+ * need to change whole approach in order to calculate align as function of
+ * current task alignment */
+
+struct compat_xt_counters
+{
+       u_int32_t cnt[4];
+};
+
+struct compat_xt_counters_info
+{
+       char name[XT_TABLE_MAXNAMELEN];
+       compat_uint_t num_counters;
+       struct compat_xt_counters counters[0];
+};
+
+#define COMPAT_XT_ALIGN(s) (((s) + (__alignof__(struct compat_xt_counters)-1)) \
+               & ~(__alignof__(struct compat_xt_counters)-1))
+
+extern void xt_compat_lock(int af);
+extern void xt_compat_unlock(int af);
+extern int xt_compat_match(void *match, void **dstptr, int *size, int convert);
+extern int xt_compat_target(void *target, void **dstptr, int *size,
+               int convert);
+
+#endif /* CONFIG_COMPAT */
 #endif /* __KERNEL__ */
 
 #endif /* _X_TABLES_H */
diff --git a/include/linux/netfilter/xt_esp.h b/include/linux/netfilter/xt_esp.h
new file mode 100644 (file)
index 0000000..9380fb1
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _XT_ESP_H
+#define _XT_ESP_H
+
+struct xt_esp
+{
+       u_int32_t spis[2];      /* Security Parameter Index */
+       u_int8_t  invflags;     /* Inverse flags */
+};
+
+/* Values for "invflags" field in struct xt_esp. */
+#define XT_ESP_INV_SPI 0x01    /* Invert the sense of spi. */
+#define XT_ESP_INV_MASK        0x01    /* All possible flags. */
+
+#endif /*_XT_ESP_H*/
diff --git a/include/linux/netfilter/xt_multiport.h b/include/linux/netfilter/xt_multiport.h
new file mode 100644 (file)
index 0000000..d49ee41
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _XT_MULTIPORT_H
+#define _XT_MULTIPORT_H
+
+enum xt_multiport_flags
+{
+       XT_MULTIPORT_SOURCE,
+       XT_MULTIPORT_DESTINATION,
+       XT_MULTIPORT_EITHER
+};
+
+#define XT_MULTI_PORTS 15
+
+/* Must fit inside union xt_matchinfo: 16 bytes */
+struct xt_multiport
+{
+       u_int8_t flags;                         /* Type of comparison */
+       u_int8_t count;                         /* Number of ports */
+       u_int16_t ports[XT_MULTI_PORTS];        /* Ports */
+};
+
+struct xt_multiport_v1
+{
+       u_int8_t flags;                         /* Type of comparison */
+       u_int8_t count;                         /* Number of ports */
+       u_int16_t ports[XT_MULTI_PORTS];        /* Ports */
+       u_int8_t pflags[XT_MULTI_PORTS];        /* Port flags */
+       u_int8_t invert;                        /* Invert flag */
+};
+
+#endif /*_XT_MULTIPORT_H*/
index d5b8c0d..c0dac16 100644 (file)
@@ -316,5 +316,23 @@ extern unsigned int ipt_do_table(struct sk_buff **pskb,
                                 void *userdata);
 
 #define IPT_ALIGN(s) XT_ALIGN(s)
+
+#ifdef CONFIG_COMPAT
+#include <net/compat.h>
+
+struct compat_ipt_entry
+{
+       struct ipt_ip ip;
+       compat_uint_t nfcache;
+       u_int16_t target_offset;
+       u_int16_t next_offset;
+       compat_uint_t comefrom;
+       struct compat_xt_counters counters;
+       unsigned char elems[0];
+};
+
+#define COMPAT_IPT_ALIGN(s)    COMPAT_XT_ALIGN(s)
+
+#endif /* CONFIG_COMPAT */
 #endif /*__KERNEL__*/
 #endif /* _IPTABLES_H */
index c782a83..78296e7 100644 (file)
@@ -1,16 +1,10 @@
 #ifndef _IPT_ESP_H
 #define _IPT_ESP_H
 
-struct ipt_esp
-{
-       u_int32_t spis[2];                      /* Security Parameter Index */
-       u_int8_t  invflags;                     /* Inverse flags */
-};
+#include <linux/netfilter/xt_esp.h>
 
-
-
-/* Values for "invflags" field in struct ipt_esp. */
-#define IPT_ESP_INV_SPI                0x01    /* Invert the sense of spi. */
-#define IPT_ESP_INV_MASK       0x01    /* All possible flags. */
+#define ipt_esp xt_esp
+#define IPT_ESP_INV_SPI                XT_ESP_INV_SPI
+#define IPT_ESP_INV_MASK       XT_ESP_INV_MASK
 
 #endif /*_IPT_ESP_H*/
index e6b6fff..55fe85e 100644 (file)
@@ -1,30 +1,15 @@
 #ifndef _IPT_MULTIPORT_H
 #define _IPT_MULTIPORT_H
-#include <linux/netfilter_ipv4/ip_tables.h>
 
-enum ipt_multiport_flags
-{
-       IPT_MULTIPORT_SOURCE,
-       IPT_MULTIPORT_DESTINATION,
-       IPT_MULTIPORT_EITHER
-};
+#include <linux/netfilter/xt_multiport.h>
 
-#define IPT_MULTI_PORTS        15
+#define IPT_MULTIPORT_SOURCE           XT_MULTIPORT_SOURCE
+#define IPT_MULTIPORT_DESTINATION      XT_MULTIPORT_DESTINATION
+#define IPT_MULTIPORT_EITHER           XT_MULTIPORT_EITHER
 
-/* Must fit inside union ipt_matchinfo: 16 bytes */
-struct ipt_multiport
-{
-       u_int8_t flags;                         /* Type of comparison */
-       u_int8_t count;                         /* Number of ports */
-       u_int16_t ports[IPT_MULTI_PORTS];       /* Ports */
-};
+#define IPT_MULTI_PORTS                        XT_MULTI_PORTS
+
+#define ipt_multiport                  xt_multiport
+#define ipt_multiport_v1               xt_multiport_v1
 
-struct ipt_multiport_v1
-{
-       u_int8_t flags;                         /* Type of comparison */
-       u_int8_t count;                         /* Number of ports */
-       u_int16_t ports[IPT_MULTI_PORTS];       /* Ports */
-       u_int8_t pflags[IPT_MULTI_PORTS];       /* Port flags */
-       u_int8_t invert;                        /* Invert flag */
-};
 #endif /*_IPT_MULTIPORT_H*/
index a91b6ab..f62eaf5 100644 (file)
@@ -1,14 +1,10 @@
 #ifndef _IP6T_ESP_H
 #define _IP6T_ESP_H
 
-struct ip6t_esp
-{
-       u_int32_t spis[2];                      /* Security Parameter Index */
-       u_int8_t  invflags;                     /* Inverse flags */
-};
+#include <linux/netfilter/xt_esp.h>
 
-/* Values for "invflags" field in struct ip6t_esp. */
-#define IP6T_ESP_INV_SPI               0x01    /* Invert the sense of spi. */
-#define IP6T_ESP_INV_MASK      0x01    /* All possible flags. */
+#define ip6t_esp xt_esp
+#define IP6T_ESP_INV_SPI       XT_ESP_INV_SPI
+#define IP6T_ESP_INV_MASK      XT_ESP_INV_MASK
 
 #endif /*_IP6T_ESP_H*/
index efe4954..042c926 100644 (file)
@@ -1,21 +1,14 @@
 #ifndef _IP6T_MULTIPORT_H
 #define _IP6T_MULTIPORT_H
-#include <linux/netfilter_ipv6/ip6_tables.h>
 
-enum ip6t_multiport_flags
-{
-       IP6T_MULTIPORT_SOURCE,
-       IP6T_MULTIPORT_DESTINATION,
-       IP6T_MULTIPORT_EITHER
-};
+#include <linux/netfilter/xt_multiport.h>
 
-#define IP6T_MULTI_PORTS       15
+#define IP6T_MULTIPORT_SOURCE          XT_MULTIPORT_SOURCE
+#define IP6T_MULTIPORT_DESTINATION     XT_MULTIPORT_DESTINATION
+#define IP6T_MULTIPORT_EITHER          XT_MULTIPORT_EITHER
 
-/* Must fit inside union ip6t_matchinfo: 16 bytes */
-struct ip6t_multiport
-{
-       u_int8_t flags;                         /* Type of comparison */
-       u_int8_t count;                         /* Number of ports */
-       u_int16_t ports[IP6T_MULTI_PORTS];      /* Ports */
-};
-#endif /*_IPT_MULTIPORT_H*/
+#define IP6T_MULTI_PORTS               XT_MULTI_PORTS
+
+#define ip6t_multiport                 xt_multiport
+
+#endif /*_IP6T_MULTIPORT_H*/
index 75c7f55..d218fc7 100644 (file)
@@ -60,5 +60,8 @@ void free_pipe_info(struct inode* inode);
  * add the splice flags here.
  */
 #define SPLICE_F_MOVE  (0x01)  /* move pages instead of copying */
+#define SPLICE_F_NONBLOCK (0x02) /* don't block on the pipe splicing (but */
+                                /* we may still block on the fd we splice */
+                                /* from/to, of course */
 
 #endif
index 690aabc..6348e83 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/device.h>
 #include <linux/mod_devicetable.h>
 
@@ -42,7 +43,7 @@ struct serio {
        struct serio *parent, *child;
 
        struct serio_driver *drv;       /* accessed from interrupt, must be protected by serio->lock and serio->sem */
-       struct semaphore drv_sem;       /* protects serio->drv so attributes can pin driver */
+       struct mutex drv_mutex;         /* protects serio->drv so attributes can pin driver */
 
        struct device dev;
        unsigned int registered;        /* port has been fully registered with driver core */
@@ -151,17 +152,17 @@ static inline void serio_continue_rx(struct serio *serio)
  */
 static inline int serio_pin_driver(struct serio *serio)
 {
-       return down_interruptible(&serio->drv_sem);
+       return mutex_lock_interruptible(&serio->drv_mutex);
 }
 
 static inline void serio_pin_driver_uninterruptible(struct serio *serio)
 {
-       down(&serio->drv_sem);
+       mutex_lock(&serio->drv_mutex);
 }
 
 static inline void serio_unpin_driver(struct serio *serio)
 {
-       up(&serio->drv_sem);
+       mutex_unlock(&serio->drv_mutex);
 }
 
 
index 0ff7ca6..7168302 100644 (file)
@@ -20,7 +20,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  *
  * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
- * 
+ *
  * Changes/Revisions:
  *     0.2     16/10/2004 (Micah Dowty <micah@navi.cx>)
  *             - added force feedback support
@@ -51,7 +51,7 @@ struct uinput_request {
 
 struct uinput_device {
        struct input_dev        *dev;
-       struct semaphore        sem;
+       struct mutex            mutex;
        enum uinput_state       state;
        wait_queue_head_t       waitq;
        unsigned char           ready;
index af2d615..d7670ec 100644 (file)
@@ -966,66 +966,17 @@ struct v4l2_sliced_vbi_format
 
 /* Teletext World System Teletext
    (WST), defined on ITU-R BT.653-2 */
-#define V4L2_SLICED_TELETEXT_PAL_B      (0x000001)
-#define V4L2_SLICED_TELETEXT_PAL_C      (0x000002)
-#define V4L2_SLICED_TELETEXT_NTSC_B     (0x000010)
-#define V4L2_SLICED_TELETEXT_SECAM      (0x000020)
-
-/* Teletext North American Broadcast Teletext Specification
-   (NABTS), defined on ITU-R BT.653-2 */
-#define V4L2_SLICED_TELETEXT_NTSC_C     (0x000040)
-#define V4L2_SLICED_TELETEXT_NTSC_D     (0x000080)
-
+#define V4L2_SLICED_TELETEXT_B          (0x0001)
 /* Video Program System, defined on ETS 300 231*/
-#define V4L2_SLICED_VPS                 (0x000400)
-
+#define V4L2_SLICED_VPS                 (0x0400)
 /* Closed Caption, defined on EIA-608 */
-#define V4L2_SLICED_CAPTION_525         (0x001000)
-#define V4L2_SLICED_CAPTION_625         (0x002000)
-
+#define V4L2_SLICED_CAPTION_525         (0x1000)
 /* Wide Screen System, defined on ITU-R BT1119.1 */
-#define V4L2_SLICED_WSS_625             (0x004000)
-
-/* Wide Screen System, defined on IEC 61880 */
-#define V4L2_SLICED_WSS_525             (0x008000)
-
-/* Vertical Interval Timecode (VITC), defined on SMPTE 12M */
-#define V4l2_SLICED_VITC_625           (0x010000)
-#define V4l2_SLICED_VITC_525           (0x020000)
-
-#define V4L2_SLICED_TELETEXT_B         (V4L2_SLICED_TELETEXT_PAL_B  |\
-                                        V4L2_SLICED_TELETEXT_NTSC_B)
-
-#define V4L2_SLICED_TELETEXT           (V4L2_SLICED_TELETEXT_PAL_B  |\
-                                        V4L2_SLICED_TELETEXT_PAL_C  |\
-                                        V4L2_SLICED_TELETEXT_SECAM  |\
-                                        V4L2_SLICED_TELETEXT_NTSC_B |\
-                                        V4L2_SLICED_TELETEXT_NTSC_C |\
-                                        V4L2_SLICED_TELETEXT_NTSC_D)
-
-#define V4L2_SLICED_CAPTION            (V4L2_SLICED_CAPTION_525     |\
-                                        V4L2_SLICED_CAPTION_625)
-
-#define V4L2_SLICED_WSS                        (V4L2_SLICED_WSS_525         |\
-                                        V4L2_SLICED_WSS_625)
-
-#define V4L2_SLICED_VITC               (V4L2_SLICED_VITC_525        |\
-                                        V4L2_SLICED_VITC_625)
-
-#define V4L2_SLICED_VBI_525             (V4L2_SLICED_TELETEXT_NTSC_B |\
-                                        V4L2_SLICED_TELETEXT_NTSC_C |\
-                                        V4L2_SLICED_TELETEXT_NTSC_D |\
-                                        V4L2_SLICED_CAPTION_525     |\
-                                        V4L2_SLICED_WSS_525         |\
-                                        V4l2_SLICED_VITC_525)
-
-#define V4L2_SLICED_VBI_625             (V4L2_SLICED_TELETEXT_PAL_B  |\
-                                        V4L2_SLICED_TELETEXT_PAL_C  |\
-                                        V4L2_SLICED_TELETEXT_SECAM  |\
-                                        V4L2_SLICED_VPS             |\
-                                        V4L2_SLICED_CAPTION_625     |\
-                                        V4L2_SLICED_WSS_625         |\
-                                        V4l2_SLICED_VITC_625)
+#define V4L2_SLICED_WSS_625             (0x4000)
+
+#define V4L2_SLICED_VBI_525             (V4L2_SLICED_CAPTION_525)
+#define V4L2_SLICED_VBI_625             (V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625)
+
 
 struct v4l2_sliced_vbi_cap
 {
diff --git a/include/media/cx25840.h b/include/media/cx25840.h
new file mode 100644 (file)
index 0000000..8e7e52d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+    cx25840.h - definition for cx25840/1/2/3 inputs
+
+    Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _CX25840_H_
+#define _CX25840_H_
+
+enum cx25840_video_input {
+       /* Composite video inputs In1-In8 */
+       CX25840_COMPOSITE1 = 1,
+       CX25840_COMPOSITE2,
+       CX25840_COMPOSITE3,
+       CX25840_COMPOSITE4,
+       CX25840_COMPOSITE5,
+       CX25840_COMPOSITE6,
+       CX25840_COMPOSITE7,
+       CX25840_COMPOSITE8,
+
+       /* S-Video inputs consist of one luma input (In1-In4) ORed with one
+          chroma input (In5-In8) */
+       CX25840_SVIDEO_LUMA1 = 0x10,
+       CX25840_SVIDEO_LUMA2 = 0x20,
+       CX25840_SVIDEO_LUMA3 = 0x30,
+       CX25840_SVIDEO_LUMA4 = 0x40,
+       CX25840_SVIDEO_CHROMA4 = 0x400,
+       CX25840_SVIDEO_CHROMA5 = 0x500,
+       CX25840_SVIDEO_CHROMA6 = 0x600,
+       CX25840_SVIDEO_CHROMA7 = 0x700,
+       CX25840_SVIDEO_CHROMA8 = 0x800,
+
+       /* S-Video aliases for common luma/chroma combinations */
+       CX25840_SVIDEO1 = 0x510,
+       CX25840_SVIDEO2 = 0x620,
+       CX25840_SVIDEO3 = 0x730,
+       CX25840_SVIDEO4 = 0x840,
+};
+
+enum cx25840_audio_input {
+       /* Audio inputs: serial or In4-In8 */
+       CX25840_AUDIO_SERIAL,
+       CX25840_AUDIO4 = 4,
+       CX25840_AUDIO5,
+       CX25840_AUDIO6,
+       CX25840_AUDIO7,
+       CX25840_AUDIO8,
+};
+
+#endif
index 0be61a0..6ab8549 100644 (file)
  */
 
 /* SCART input to DSP selection */
-#define MSP_IN_SCART_1         0  /* Pin SC1_IN */
-#define MSP_IN_SCART_2         1  /* Pin SC2_IN */
-#define MSP_IN_SCART_3         2  /* Pin SC3_IN */
-#define MSP_IN_SCART_4         3  /* Pin SC4_IN */
+#define MSP_IN_SCART1                  0  /* Pin SC1_IN */
+#define MSP_IN_SCART2                  1  /* Pin SC2_IN */
+#define MSP_IN_SCART3                  2  /* Pin SC3_IN */
+#define MSP_IN_SCART4                  3  /* Pin SC4_IN */
 #define MSP_IN_MONO            6  /* Pin MONO_IN */
 #define MSP_IN_MUTE            7  /* Mute DSP input */
 #define MSP_SCART_TO_DSP(in)   (in)
 /* Tuner input to demodulator and DSP selection */
-#define MSP_IN_TUNER_1                 0  /* Analog Sound IF input pin ANA_IN1 */
-#define MSP_IN_TUNER_2         1  /* Analog Sound IF input pin ANA_IN2 */
+#define MSP_IN_TUNER1          0  /* Analog Sound IF input pin ANA_IN1 */
+#define MSP_IN_TUNER         1  /* Analog Sound IF input pin ANA_IN2 */
 #define MSP_TUNER_TO_DSP(in)   ((in) << 3)
 
 /* The msp has up to 5 DSP outputs, each output can independently select
    DSP. This is currently not implemented. Also not implemented is the
    multi-channel capable I2S3 input of the 44x0G. If someone can demonstrate
    a need for one of those features then additional support can be added. */
-#define MSP_DSP_OUT_TUNER      0  /* Tuner output */
-#define MSP_DSP_OUT_SCART      2  /* SCART output */
-#define MSP_DSP_OUT_I2S1       5  /* I2S1 output */
-#define MSP_DSP_OUT_I2S2       6  /* I2S2 output */
-#define MSP_DSP_OUT_I2S3       7  /* I2S3 output */
-#define MSP_DSP_OUT_MAIN_AVC   11 /* MAIN AVC processed output */
-#define MSP_DSP_OUT_MAIN       12 /* MAIN output */
-#define MSP_DSP_OUT_AUX        13 /* AUX output */
+#define MSP_DSP_IN_TUNER       0  /* Tuner DSP input */
+#define MSP_DSP_IN_SCART       2  /* SCART DSP input */
+#define MSP_DSP_IN_I2S1        5  /* I2S1 DSP input */
+#define MSP_DSP_IN_I2S2        6  /* I2S2 DSP input */
+#define MSP_DSP_IN_I2S3        7  /* I2S3 DSP input */
+#define MSP_DSP_IN_MAIN_AVC    11 /* MAIN AVC processed DSP input */
+#define MSP_DSP_IN_MAIN        12 /* MAIN DSP input */
+#define MSP_DSP_IN_AUX                 13 /* AUX DSP input */
 #define MSP_DSP_TO_MAIN(in)    ((in) << 4)
 #define MSP_DSP_TO_AUX(in)     ((in) << 8)
 #define MSP_DSP_TO_SCART1(in)  ((in) << 12)
 
 /* Output SCART select: the SCART outputs can select which input
    to use. */
-#define MSP_OUT_SCART1                 0  /* SCART1 input, bypassing the DSP */
-#define MSP_OUT_SCART2                 1  /* SCART2 input, bypassing the DSP */
-#define MSP_OUT_SCART3                 2  /* SCART3 input, bypassing the DSP */
-#define MSP_OUT_SCART4                 3  /* SCART4 input, bypassing the DSP */
-#define MSP_OUT_SCART1_DA      4  /* DSP SCART1 output */
-#define MSP_OUT_SCART2_DA      5  /* DSP SCART2 output */
-#define MSP_OUT_MONO           6  /* MONO input, bypassing the DSP */
-#define MSP_OUT_MUTE           7  /* MUTE output */
-#define MSP_OUT_TO_SCART1(in)          (in)
-#define MSP_OUT_TO_SCART2(in)          ((in) << 4)
+#define MSP_SC_IN_SCART1       0  /* SCART1 input, bypassing the DSP */
+#define MSP_SC_IN_SCART2       1  /* SCART2 input, bypassing the DSP */
+#define MSP_SC_IN_SCART3       2  /* SCART3 input, bypassing the DSP */
+#define MSP_SC_IN_SCART4       3  /* SCART4 input, bypassing the DSP */
+#define MSP_SC_IN_DSP_SCART1   4  /* DSP SCART1 input */
+#define MSP_SC_IN_DSP_SCART2   5  /* DSP SCART2 input */
+#define MSP_SC_IN_MONO                 6  /* MONO input, bypassing the DSP */
+#define MSP_SC_IN_MUTE                 7  /* MUTE output */
+#define MSP_SC_TO_SCART1(in)   (in)
+#define MSP_SC_TO_SCART2(in)   ((in) << 4)
 
 /* Shortcut macros */
 #define MSP_INPUT(sc, t, main_aux_src, sc_i2s_src) \
         MSP_DSP_TO_SCART1(sc_i2s_src) | \
         MSP_DSP_TO_SCART2(sc_i2s_src) | \
         MSP_DSP_TO_I2S(sc_i2s_src))
-#define MSP_INPUT_DEFAULT MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, \
-                                   MSP_DSP_OUT_TUNER, MSP_DSP_OUT_TUNER)
+#define MSP_INPUT_DEFAULT MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+                                   MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER)
 #define MSP_OUTPUT(sc) \
-       (MSP_OUT_TO_SCART1(sc) | \
-        MSP_OUT_TO_SCART2(sc))
+       (MSP_SC_TO_SCART1(sc) | \
+        MSP_SC_TO_SCART2(sc))
 /* This equals the RESET position of the msp3400 ACB register */
-#define MSP_OUTPUT_DEFAULT (MSP_OUT_TO_SCART1(MSP_OUT_SCART3) | \
-                           MSP_OUT_TO_SCART2(MSP_OUT_SCART1_DA))
+#define MSP_OUTPUT_DEFAULT (MSP_SC_TO_SCART1(MSP_SC_IN_SCART3) | \
+                           MSP_SC_TO_SCART2(MSP_SC_IN_DSP_SCART1))
 
 /* Tuner inputs vs. msp version */
 /* Chip      TUNER_1   TUNER_2
diff --git a/include/media/saa7115.h b/include/media/saa7115.h
new file mode 100644 (file)
index 0000000..6b4836f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+    saa7115.h - definition for saa7113/4/5 inputs
+
+    Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _SAA7115_H_
+#define _SAA7115_H_
+
+/* SAA7113/4/5 HW inputs */
+#define SAA7115_COMPOSITE0 0
+#define SAA7115_COMPOSITE1 1
+#define SAA7115_COMPOSITE2 2
+#define SAA7115_COMPOSITE3 3
+#define SAA7115_COMPOSITE4 4 /* not available for the saa7113 */
+#define SAA7115_COMPOSITE5 5 /* not available for the saa7113 */
+#define SAA7115_SVIDEO0    6
+#define SAA7115_SVIDEO1    7
+#define SAA7115_SVIDEO2    8
+#define SAA7115_SVIDEO3    9
+
+#endif
+
diff --git a/include/media/saa7127.h b/include/media/saa7127.h
new file mode 100644 (file)
index 0000000..bbcf862
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+    saa7127.h - definition for saa7126/7/8/9 inputs/outputs
+
+    Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _SAA7127_H_
+#define _SAA7127_H_
+
+/* Enumeration for the supported input types */
+enum saa7127_input_type {
+       SAA7127_INPUT_TYPE_NORMAL,
+       SAA7127_INPUT_TYPE_TEST_IMAGE
+};
+
+/* Enumeration for the supported output signal types */
+enum saa7127_output_type {
+       SAA7127_OUTPUT_TYPE_BOTH,
+       SAA7127_OUTPUT_TYPE_COMPOSITE,
+       SAA7127_OUTPUT_TYPE_SVIDEO,
+       SAA7127_OUTPUT_TYPE_RGB,
+       SAA7127_OUTPUT_TYPE_YUV_C,
+       SAA7127_OUTPUT_TYPE_YUV_V
+};
+
+#endif
+
diff --git a/include/media/upd64031a.h b/include/media/upd64031a.h
new file mode 100644 (file)
index 0000000..3ad6a32
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * upd64031a - NEC Electronics Ghost Reduction input defines
+ *
+ * 2006 by Hans Verkuil (hverkuil@xs4all.nl)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _UPD64031A_H_
+#define _UPD64031A_H_
+
+/* Ghost reduction modes */
+#define UPD64031A_GR_ON        0
+#define UPD64031A_GR_OFF       1
+#define UPD64031A_GR_THROUGH   3
+
+/* Direct 3D/YCS Connection */
+#define UPD64031A_3DYCS_DISABLE   (0 << 2)
+#define UPD64031A_3DYCS_COMPOSITE (2 << 2)
+#define UPD64031A_3DYCS_SVIDEO    (3 << 2)
+
+/* Composite sync digital separation circuit */
+#define UPD64031A_COMPOSITE_EXTERNAL (1 << 4)
+
+/* Vertical sync digital separation circuit */
+#define UPD64031A_VERTICAL_EXTERNAL (1 << 5)
+
+#endif
diff --git a/include/media/upd64083.h b/include/media/upd64083.h
new file mode 100644 (file)
index 0000000..59b6f32
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * upd6408x - NEC Electronics 3-Dimensional Y/C separation input defines
+ *
+ * 2006 by Hans Verkuil (hverkuil@xs4all.nl)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _UPD64083_H_
+#define _UPD64083_H_
+
+/* There are two bits of information that the driver needs in order
+   to select the correct routing: the operating mode and the selection
+   of the Y input (external or internal).
+
+   The first two operating modes expect a composite signal on the Y input,
+   the second two operating modes use both the Y and C inputs.
+
+   Normally YCS_MODE is used for tuner and composite inputs, and the
+   YCNR mode is used for S-Video inputs.
+
+   The external Y-ADC is selected when the composite input comes from a
+   upd64031a ghost reduction device. If this device is not present, or
+   the input is a S-Video signal, then the internal Y-ADC input should
+   be used. */
+
+/* Operating modes: */
+
+/* YCS mode: Y/C separation (burst locked clocking) */
+#define UPD64083_YCS_MODE      0
+/* YCS+ mode: 2D Y/C separation and YCNR (burst locked clocking) */
+#define UPD64083_YCS_PLUS_MODE 1
+
+/* Note: the following two modes cannot be used in combination with the
+   external Y-ADC. */
+/* MNNR mode: frame comb type YNR+C delay (line locked clocking) */
+#define UPD64083_MNNR_MODE     2
+/* YCNR mode: frame recursive YCNR (burst locked clocking) */
+#define UPD64083_YCNR_MODE     3
+
+/* Select external Y-ADC: this should be set if this device is used in
+   combination with the upd64031a ghost reduction device.
+   Otherwise leave at 0 (use internal Y-ADC). */
+#define UPD64083_EXT_Y_ADC     (1 << 2)
+
+#endif
index e100291..0d5529c 100644 (file)
@@ -242,7 +242,6 @@ extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
 
 extern void xfrm_state_delete_tunnel(struct xfrm_state *x);
 
-struct xfrm_decap_state;
 struct xfrm_type
 {
        char                    *description;
@@ -251,7 +250,7 @@ struct xfrm_type
 
        int                     (*init_state)(struct xfrm_state *x);
        void                    (*destructor)(struct xfrm_state *);
-       int                     (*input)(struct xfrm_state *, struct xfrm_decap_state *, struct sk_buff *skb);
+       int                     (*input)(struct xfrm_state *, struct sk_buff *skb);
        int                     (*output)(struct xfrm_state *, struct sk_buff *pskb);
        /* Estimate maximal size of result of transformation of a dgram */
        u32                     (*get_max_size)(struct xfrm_state *, int size);
@@ -606,25 +605,11 @@ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
 
 extern void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
 
-/* Decapsulation state, used by the input to store data during
- * decapsulation procedure, to be used later (during the policy
- * check
- */
-struct xfrm_decap_state {
-       char    decap_data[20];
-       __u16   decap_type;
-};   
-
-struct sec_decap_state {
-       struct xfrm_state       *xvec;
-       struct xfrm_decap_state decap;
-};
-
 struct sec_path
 {
        atomic_t                refcnt;
        int                     len;
-       struct sec_decap_state  x[XFRM_MAX_DEPTH];
+       struct xfrm_state       *xvec[XFRM_MAX_DEPTH];
 };
 
 static inline struct sec_path *
index b53b78d..6bc7472 100644 (file)
@@ -35,7 +35,7 @@ typedef struct region_info_t {
 #define REGION_BAR_MASK                0xe000
 #define REGION_BAR_SHIFT       13
 
-int pcmcia_get_first_region(client_handle_t handle, region_info_t *rgn);
-int pcmcia_get_next_region(client_handle_t handle, region_info_t *rgn);
+int pcmcia_get_first_region(struct pcmcia_device *handle, region_info_t *rgn);
+int pcmcia_get_next_region(struct pcmcia_device *handle, region_info_t *rgn);
 
 #endif /* _LINUX_BULKMEM_H */
index da19c29..c1da855 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * ciscode.h -- Definitions for bulk memory services
+ * ciscode.h
  *
  * 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
 
 #define MANFID_XIRCOM                  0x0105
 
+#define MANFID_POSSIO                  0x030c
+#define PRODID_POSSIO_GCC              0x0003
+
 #endif /* _LINUX_CISCODE_H */
index c6a0695..d3bbb19 100644 (file)
@@ -586,12 +586,7 @@ typedef struct cisdump_t {
     cisdata_t  Data[CISTPL_MAX_CIS_SIZE];
 } cisdump_t;
 
-int pcmcia_get_first_tuple(client_handle_t handle, tuple_t *tuple);
-int pcmcia_get_next_tuple(client_handle_t handle, tuple_t *tuple);
-int pcmcia_get_tuple_data(client_handle_t handle, tuple_t *tuple);
-int pcmcia_parse_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse);
 
-int pcmcia_validate_cis(client_handle_t handle, cisinfo_t *info);
 int pcmcia_replace_cis(struct pcmcia_socket *s, cisdump_t *cis);
 
 /* don't use outside of PCMCIA core yet */
@@ -602,4 +597,20 @@ int pccard_parse_tuple(tuple_t *tuple, cisparse_t *parse);
 
 int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, cisinfo_t *info);
 
+/* ... but use these wrappers instead */
+#define pcmcia_get_first_tuple(p_dev, tuple) \
+               pccard_get_first_tuple(p_dev->socket, p_dev->func, tuple)
+
+#define pcmcia_get_next_tuple(p_dev, tuple) \
+               pccard_get_next_tuple(p_dev->socket, p_dev->func, tuple)
+
+#define pcmcia_get_tuple_data(p_dev, tuple) \
+               pccard_get_tuple_data(p_dev->socket, tuple)
+
+#define pcmcia_parse_tuple(p_dev, tuple, parse) \
+               pccard_parse_tuple(tuple, parse)
+
+#define pcmcia_validate_cis(p_dev, info) \
+               pccard_validate_cis(p_dev->socket, p_dev->func, info)
+
 #endif /* LINUX_CISTPL_H */
index 52660f3..d5838c3 100644 (file)
@@ -109,17 +109,6 @@ typedef struct client_req_t {
 
 #define CLIENT_THIS_SOCKET     0x01
 
-/* For RegisterClient */
-typedef struct client_reg_t {
-    dev_info_t *dev_info;
-    u_int      Attributes;  /* UNUSED */
-    u_int      EventMask;
-    int                (*event_handler)(event_t event, int priority,
-                                event_callback_args_t *);
-    event_callback_args_t event_callback_args;
-    u_int      Version;
-} client_reg_t;
-
 /* ModifyConfiguration */
 typedef struct modconf_t {
     u_int      Attributes;
@@ -127,15 +116,16 @@ typedef struct modconf_t {
 } modconf_t;
 
 /* Attributes for ModifyConfiguration */
-#define CONF_IRQ_CHANGE_VALID  0x100
-#define CONF_VCC_CHANGE_VALID  0x200
-#define CONF_VPP1_CHANGE_VALID 0x400
-#define CONF_VPP2_CHANGE_VALID 0x800
+#define CONF_IRQ_CHANGE_VALID  0x0100
+#define CONF_VCC_CHANGE_VALID  0x0200
+#define CONF_VPP1_CHANGE_VALID 0x0400
+#define CONF_VPP2_CHANGE_VALID 0x0800
+#define CONF_IO_CHANGE_WIDTH   0x1000
 
 /* For RequestConfiguration */
 typedef struct config_req_t {
     u_int      Attributes;
-    u_int      Vcc, Vpp1, Vpp2;
+    u_int      Vpp; /* both Vpp1 and Vpp2 */
     u_int      IntType;
     u_int      ConfigBase;
     u_char     Status, Pin, Copy, ExtStatus;
@@ -389,23 +379,27 @@ int pcmcia_get_status(struct pcmcia_device *p_dev, cs_status_t *status);
 int pcmcia_get_mem_page(window_handle_t win, memreq_t *req);
 int pcmcia_map_mem_page(window_handle_t win, memreq_t *req);
 int pcmcia_modify_configuration(struct pcmcia_device *p_dev, modconf_t *mod);
-int pcmcia_release_configuration(struct pcmcia_device *p_dev);
-int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req);
-int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req);
 int pcmcia_release_window(window_handle_t win);
 int pcmcia_request_configuration(struct pcmcia_device *p_dev, config_req_t *req);
 int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req);
 int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req);
 int pcmcia_request_window(struct pcmcia_device **p_dev, win_req_t *req, window_handle_t *wh);
-int pcmcia_reset_card(struct pcmcia_device *p_dev, client_req_t *req);
 int pcmcia_suspend_card(struct pcmcia_socket *skt);
 int pcmcia_resume_card(struct pcmcia_socket *skt);
 int pcmcia_eject_card(struct pcmcia_socket *skt);
 int pcmcia_insert_card(struct pcmcia_socket *skt);
+int pccard_reset_card(struct pcmcia_socket *skt);
+
+struct pcmcia_device * pcmcia_dev_present(struct pcmcia_device *p_dev);
+void pcmcia_disable_device(struct pcmcia_device *p_dev);
 
 struct pcmcia_socket * pcmcia_get_socket(struct pcmcia_socket *skt);
 void pcmcia_put_socket(struct pcmcia_socket *skt);
 
+/* compatibility functions */
+#define pcmcia_reset_card(p_dev, req) \
+               pccard_reset_card(p_dev->socket)
+
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_CS_H */
index 8e2a963..8c339f5 100644 (file)
@@ -39,7 +39,7 @@ typedef struct win_info_t {
 typedef struct bind_info_t {
     dev_info_t         dev_info;
     u_char             function;
-    struct dev_link_t  *instance;
+    struct pcmcia_device *instance;
     char               name[DEV_NAME_LEN];
     u_short            major, minor;
     void               *next;
@@ -96,6 +96,7 @@ typedef union ds_ioctl_arg_t {
 
 #ifdef __KERNEL__
 #include <linux/device.h>
+#include <pcmcia/ss.h>
 
 typedef struct dev_node_t {
     char               dev_name[DEV_NAME_LEN];
@@ -103,34 +104,9 @@ typedef struct dev_node_t {
     struct dev_node_t  *next;
 } dev_node_t;
 
-typedef struct dev_link_t {
-    dev_node_t         *dev;
-    u_int              state, open;
-    wait_queue_head_t  pending;
-    client_handle_t    handle;
-    io_req_t           io;
-    irq_req_t          irq;
-    config_req_t       conf;
-    window_handle_t    win;
-    void               *priv;
-    struct dev_link_t  *next;
-} dev_link_t;
-
-/* Flags for device state */
-#define DEV_PRESENT            0x01
-#define DEV_CONFIG             0x02
-#define DEV_STALE_CONFIG       0x04    /* release on close */
-#define DEV_STALE_LINK         0x08    /* detach on release */
-#define DEV_CONFIG_PENDING     0x10
-#define DEV_RELEASE_PENDING    0x20
-#define DEV_SUSPEND            0x40
-#define DEV_BUSY               0x80
-
-#define DEV_OK(l) \
-    ((l) && ((l->state & ~DEV_BUSY) == (DEV_CONFIG|DEV_PRESENT)))
-
 
 struct pcmcia_socket;
+struct config_t;
 
 struct pcmcia_driver {
        int (*probe)            (struct pcmcia_device *dev);
@@ -148,6 +124,7 @@ struct pcmcia_driver {
 int pcmcia_register_driver(struct pcmcia_driver *driver);
 void pcmcia_unregister_driver(struct pcmcia_driver *driver);
 
+
 struct pcmcia_device {
        /* the socket and the device_no [for multifunction devices]
           uniquely define a pcmcia_device */
@@ -160,21 +137,40 @@ struct pcmcia_device {
        /* the hardware "function" device; certain subdevices can
         * share one hardware "function" device. */
        u8                      func;
+       struct config_t*        function_config;
 
        struct list_head        socket_device_list;
 
-       /* deprecated, a cleaned up version will be moved into this
-          struct soon */
-       dev_link_t              *instance;
-       u_int                   state;
+       /* deprecated, will be cleaned up soon */
+       dev_node_t              *dev_node;
+       u_int                   open;
+       io_req_t                io;
+       irq_req_t               irq;
+       config_req_t            conf;
+       window_handle_t         win;
+
+       /* Is the device suspended, or in the process of
+        * being removed? */
+       u16                     suspended:1;
+       u16                     _removed:1;
+
+       /* Flags whether io, irq, win configurations were
+        * requested, and whether the configuration is "locked" */
+       u16                     _irq:1;
+       u16                     _io:1;
+       u16                     _win:4;
+       u16                     _locked:1;
+
+       /* Flag whether a "fuzzy" func_id based match is
+        * allowed. */
+       u16                     allow_func_id_match:1;
 
        /* information about this device */
-       u                     has_manf_id:1;
-       u                     has_card_id:1;
-       u                     has_func_id:1;
+       u16                     has_manf_id:1;
+       u16                     has_card_id:1;
+       u16                     has_func_id:1;
 
-       u8                      allow_func_id_match:1;
-       u8                      reserved:4;
+       u16                     reserved:3;
 
        u8                      func_id;
        u16                     manf_id;
@@ -182,22 +178,24 @@ struct pcmcia_device {
 
        char *                  prod_id[4];
 
+       struct device           dev;
+
+#ifdef CONFIG_PCMCIA_IOCTL
        /* device driver wanted by cardmgr */
        struct pcmcia_driver *  cardmgr;
+#endif
 
-       struct device           dev;
+       /* data private to drivers */
+       void                    *priv;
 };
 
 #define to_pcmcia_dev(n) container_of(n, struct pcmcia_device, dev)
 #define to_pcmcia_drv(n) container_of(n, struct pcmcia_driver, drv)
 
-#define handle_to_pdev(handle) (handle)
 #define handle_to_dev(handle) (handle->dev)
 
-#define dev_to_instance(dev) (dev->instance)
-
 /* error reporting */
-void cs_error(client_handle_t handle, int func, int ret);
+void cs_error(struct pcmcia_device *handle, int func, int ret);
 
 #endif /* __KERNEL__ */
 #endif /* _LINUX_DS_H */
index 2889a69..5e0a01a 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/config.h>
 #include <linux/device.h>
 #include <linux/sched.h>       /* task_struct, completion */
+#include <linux/mutex.h>
 
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
@@ -146,14 +147,15 @@ extern struct pccard_resource_ops pccard_static_ops;
 /* !SS_CAP_STATIC_MAP */
 extern struct pccard_resource_ops pccard_nonstatic_ops;
 
+/* static mem, dynamic IO sockets */
+extern struct pccard_resource_ops pccard_iodyn_ops;
+
 /*
  *  Calls to set up low-level "Socket Services" drivers
  */
 struct pcmcia_socket;
 
 typedef struct io_window_t {
-       u_int                   Attributes;
-       kio_addr_t              BasePort, NumPorts;
        kio_addr_t              InUse, Config;
        struct resource         *res;
 } io_window_t;
@@ -162,7 +164,7 @@ typedef struct io_window_t {
 typedef struct window_t {
        u_short                 magic;
        u_short                 index;
-       client_handle_t         handle;
+       struct pcmcia_device    *handle;
        struct pcmcia_socket    *sock;
        pccard_mem_map          ctl;
 } window_t;
@@ -186,7 +188,6 @@ struct pcmcia_socket {
        u_short                         lock_count;
        pccard_mem_map                  cis_mem;
        void __iomem                    *cis_virt;
-       struct config_t                 *config;
        struct {
                u_int                   AssignedIRQ;
                u_int                   Config;
@@ -241,7 +242,7 @@ struct pcmcia_socket {
 #endif
 
        /* state thread */
-       struct semaphore                skt_sem;        /* protects socket h/w state */
+       struct mutex                    skt_mutex;      /* protects socket h/w state */
 
        struct task_struct              *thread;
        struct completion               thread_done;
index 7f32c12..d1d043f 100644 (file)
@@ -170,7 +170,7 @@ static inline void snd_power_change_state(struct snd_card *card, unsigned int st
 }
 
 /* init.c */
-int snd_power_wait(struct snd_card *card, unsigned int power_state, struct file *file);
+int snd_power_wait(struct snd_card *card, unsigned int power_state);
 
 #else /* ! CONFIG_PM */
 
index 15b8856..66b1f08 100644 (file)
@@ -369,6 +369,7 @@ struct snd_pcm_substream {
        /* -- assigned files -- */
        struct snd_pcm_file *file;
        struct file *ffile;
+       void (*pcm_release)(struct snd_pcm_substream *);
 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
        /* -- OSS things -- */
        struct snd_pcm_oss_substream oss;
@@ -381,13 +382,10 @@ struct snd_pcm_substream {
        struct snd_info_entry *proc_prealloc_entry;
        /* misc flags */
        unsigned int no_mmap_ctrl: 1;
+       unsigned int hw_opened: 1;
 };
 
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
-#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL || ((substream)->oss.file != NULL))
-#else
 #define SUBSTREAM_BUSY(substream) ((substream)->file != NULL)
-#endif
 
 
 struct snd_pcm_str {
@@ -460,7 +458,6 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
                      struct snd_pcm_info __user *info);
 int snd_pcm_status(struct snd_pcm_substream *substream,
                   struct snd_pcm_status *status);
-int snd_pcm_prepare(struct snd_pcm_substream *substream);
 int snd_pcm_start(struct snd_pcm_substream *substream);
 int snd_pcm_stop(struct snd_pcm_substream *substream, int status);
 int snd_pcm_drain_done(struct snd_pcm_substream *substream);
@@ -468,11 +465,13 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream);
 int snd_pcm_suspend(struct snd_pcm_substream *substream);
 int snd_pcm_suspend_all(struct snd_pcm *pcm);
 #endif
-int snd_pcm_kernel_playback_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg);
-int snd_pcm_kernel_capture_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg);
 int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg);
-int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, struct snd_pcm_substream **rsubstream);
+int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, struct file *file,
+                          struct snd_pcm_substream **rsubstream);
 void snd_pcm_release_substream(struct snd_pcm_substream *substream);
+int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, struct file *file,
+                            struct snd_pcm_substream **rsubstream);
+void snd_pcm_detach_substream(struct snd_pcm_substream *substream);
 void snd_pcm_vma_notify_data(void *client, void *data);
 int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area);
 
index bff0778..39df2ba 100644 (file)
@@ -69,8 +69,7 @@ struct snd_pcm_oss_file {
 
 struct snd_pcm_oss_substream {
        unsigned oss: 1;                        /* oss mode */
-       struct snd_pcm_oss_setup *setup;                /* active setup */
-       struct snd_pcm_oss_file *file;
+       struct snd_pcm_oss_setup setup;         /* active setup */
 };
 
 struct snd_pcm_oss_stream {
index f806a2e..6b0c9af 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -91,8 +91,8 @@ static inline int shm_addid(struct shmid_kernel *shp)
 static inline void shm_inc (int id) {
        struct shmid_kernel *shp;
 
-       if(!(shp = shm_lock(id)))
-               BUG();
+       shp = shm_lock(id);
+       BUG_ON(!shp);
        shp->shm_atim = get_seconds();
        shp->shm_lprid = current->tgid;
        shp->shm_nattch++;
@@ -142,8 +142,8 @@ static void shm_close (struct vm_area_struct *shmd)
 
        mutex_lock(&shm_ids.mutex);
        /* remove from the list of attaches of the shm segment */
-       if(!(shp = shm_lock(id)))
-               BUG();
+       shp = shm_lock(id);
+       BUG_ON(!shp);
        shp->shm_lprid = current->tgid;
        shp->shm_dtim = get_seconds();
        shp->shm_nattch--;
@@ -283,8 +283,7 @@ asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
                err = -EEXIST;
        } else {
                shp = shm_lock(id);
-               if(shp==NULL)
-                       BUG();
+               BUG_ON(shp==NULL);
                if (shp->shm_segsz < size)
                        err = -EINVAL;
                else if (ipcperms(&shp->shm_perm, shmflg))
@@ -774,8 +773,8 @@ invalid:
        up_write(&current->mm->mmap_sem);
 
        mutex_lock(&shm_ids.mutex);
-       if(!(shp = shm_lock(shmid)))
-               BUG();
+       shp = shm_lock(shmid);
+       BUG_ON(!shp);
        shp->shm_nattch--;
        if(shp->shm_nattch == 0 &&
           shp->shm_perm.mode & SHM_DEST)
index 23151ef..5e785a2 100644 (file)
@@ -266,8 +266,7 @@ struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
 {
        struct kern_ipc_perm* p;
        int lid = id % SEQ_MULTIPLIER;
-       if(lid >= ids->entries->size)
-               BUG();
+       BUG_ON(lid >= ids->entries->size);
 
        /* 
         * do not need a rcu_dereference()() here to force ordering
@@ -275,8 +274,7 @@ struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
         */     
        p = ids->entries->p[lid];
        ids->entries->p[lid] = NULL;
-       if(p==NULL)
-               BUG();
+       BUG_ON(p==NULL);
        ids->in_use--;
 
        if (lid == ids->max_id) {
index 9fd8d4f..ce0dfb8 100644 (file)
@@ -41,7 +41,7 @@ config SOFTWARE_SUSPEND
        depends on PM && SWAP && (X86 && (!SMP || SUSPEND_SMP)) || ((FRV || PPC32) && !SMP)
        ---help---
          Enable the possibility of suspending the machine.
-         It doesn't need APM.
+         It doesn't need ACPI or APM.
          You may suspend your machine by 'swsusp' or 'shutdown -z <time>' 
          (patch for sysvinit needed). 
 
index 8cc1943..c056f33 100644 (file)
@@ -360,8 +360,7 @@ static void call_console_drivers(unsigned long start, unsigned long end)
        unsigned long cur_index, start_print;
        static int msg_level = -1;
 
-       if (((long)(start - end)) > 0)
-               BUG();
+       BUG_ON(((long)(start - end)) > 0);
 
        cur_index = start;
        start_print = start;
@@ -708,8 +707,7 @@ int __init add_preferred_console(char *name, int idx, char *options)
  */
 void acquire_console_sem(void)
 {
-       if (in_interrupt())
-               BUG();
+       BUG_ON(in_interrupt());
        down(&console_sem);
        console_locked = 1;
        console_may_schedule = 1;
index 86a7f6c..0eeb7e6 100644 (file)
@@ -30,8 +30,7 @@
  */
 void __ptrace_link(task_t *child, task_t *new_parent)
 {
-       if (!list_empty(&child->ptrace_list))
-               BUG();
+       BUG_ON(!list_empty(&child->ptrace_list));
        if (child->parent == new_parent)
                return;
        list_add(&child->ptrace_list, &child->parent->ptrace_children);
index 92025b1..5ccaac5 100644 (file)
@@ -769,8 +769,7 @@ specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
 {
        int ret = 0;
 
-       if (!irqs_disabled())
-               BUG();
+       BUG_ON(!irqs_disabled());
        assert_spin_locked(&t->sighand->siglock);
 
        /* Short-circuit ignored signals.  */
@@ -1384,8 +1383,7 @@ send_group_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
                 * the overrun count.  Other uses should not try to
                 * send the signal multiple times.
                 */
-               if (q->info.si_code != SI_TIMER)
-                       BUG();
+               BUG_ON(q->info.si_code != SI_TIMER);
                q->info.si_overrun++;
                goto out;
        } 
index ff8e701..b00ddc7 100644 (file)
@@ -410,7 +410,7 @@ EXPORT_SYMBOL(current_kernel_time);
  * current_fs_time - Return FS time
  * @sb: Superblock.
  *
- * Return the current time truncated to the time granuality supported by
+ * Return the current time truncated to the time granularity supported by
  * the fs.
  */
 struct timespec current_fs_time(struct super_block *sb)
@@ -421,11 +421,11 @@ struct timespec current_fs_time(struct super_block *sb)
 EXPORT_SYMBOL(current_fs_time);
 
 /**
- * timespec_trunc - Truncate timespec to a granuality
+ * timespec_trunc - Truncate timespec to a granularity
  * @t: Timespec
- * @gran: Granuality in ns.
+ * @gran: Granularity in ns.
  *
- * Truncate a timespec to a granuality. gran must be smaller than a second.
+ * Truncate a timespec to a granularity. gran must be smaller than a second.
  * Always rounds down.
  *
  * This function should be only used for timestamps returned by
index 6b812c0..c3a874f 100644 (file)
@@ -1479,8 +1479,7 @@ register_time_interpolator(struct time_interpolator *ti)
        unsigned long flags;
 
        /* Sanity check */
-       if (ti->frequency == 0 || ti->mask == 0)
-               BUG();
+       BUG_ON(ti->frequency == 0 || ti->mask == 0);
 
        ti->nsec_per_cyc = ((u64)NSEC_PER_SEC << ti->shift) / ti->frequency;
        spin_lock(&time_interpolator_lock);
index 55885f6..9b274fd 100644 (file)
@@ -74,8 +74,7 @@ static void flush_all_zero_pkmaps(void)
                pkmap_count[i] = 0;
 
                /* sanity check */
-               if (pte_none(pkmap_page_table[i]))
-                       BUG();
+               BUG_ON(pte_none(pkmap_page_table[i]));
 
                /*
                 * Don't need an atomic fetch-and-clear op here;
@@ -158,8 +157,7 @@ void fastcall *kmap_high(struct page *page)
        if (!vaddr)
                vaddr = map_new_virtual(page);
        pkmap_count[PKMAP_NR(vaddr)]++;
-       if (pkmap_count[PKMAP_NR(vaddr)] < 2)
-               BUG();
+       BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2);
        spin_unlock(&kmap_lock);
        return (void*) vaddr;
 }
@@ -174,8 +172,7 @@ void fastcall kunmap_high(struct page *page)
 
        spin_lock(&kmap_lock);
        vaddr = (unsigned long)page_address(page);
-       if (!vaddr)
-               BUG();
+       BUG_ON(!vaddr);
        nr = PKMAP_NR(vaddr);
 
        /*
@@ -220,8 +217,7 @@ static __init int init_emergency_pool(void)
                return 0;
 
        page_pool = mempool_create_page_pool(POOL_SIZE, 0);
-       if (!page_pool)
-               BUG();
+       BUG_ON(!page_pool);
        printk("highmem bounce pool size: %d pages\n", POOL_SIZE);
 
        return 0;
@@ -264,8 +260,7 @@ int init_emergency_isa_pool(void)
 
        isa_page_pool = mempool_create(ISA_POOL_SIZE, mempool_alloc_pages_isa,
                                       mempool_free_pages, (void *) 0);
-       if (!isa_page_pool)
-               BUG();
+       BUG_ON(!isa_page_pool);
 
        printk("isa bounce pool size: %d pages\n", ISA_POOL_SIZE);
        return 0;
index 4f5b570..e780d19 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -294,8 +294,7 @@ void validate_mm(struct mm_struct *mm)
        i = browse_rb(&mm->mm_rb);
        if (i != mm->map_count)
                printk("map_count %d rb %d\n", mm->map_count, i), bug = 1;
-       if (bug)
-               BUG();
+       BUG_ON(bug);
 }
 #else
 #define validate_mm(mm) do { } while (0)
@@ -432,8 +431,7 @@ __insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)
        struct rb_node ** rb_link, * rb_parent;
 
        __vma = find_vma_prepare(mm, vma->vm_start,&prev, &rb_link, &rb_parent);
-       if (__vma && __vma->vm_start < vma->vm_end)
-               BUG();
+       BUG_ON(__vma && __vma->vm_start < vma->vm_end);
        __vma_link(mm, vma, prev, rb_link, rb_parent);
        mm->map_count++;
 }
@@ -813,8 +811,7 @@ try_prev:
         * (e.g. stash info in next's anon_vma_node when assigning
         * an anon_vma, or when trying vma_merge).  Another time.
         */
-       if (find_vma_prev(vma->vm_mm, vma->vm_start, &near) != vma)
-               BUG();
+       BUG_ON(find_vma_prev(vma->vm_mm, vma->vm_start, &near) != vma);
        if (!near)
                goto none;
 
index 893d767..6dcce3a 100644 (file)
@@ -258,7 +258,7 @@ static void balance_dirty_pages(struct address_space *mapping)
 /**
  * balance_dirty_pages_ratelimited_nr - balance dirty memory state
  * @mapping: address_space which was dirtied
- * @nr_pages: number of pages which the caller has just dirtied
+ * @nr_pages_dirtied: number of pages which the caller has just dirtied
  *
  * Processes which are dirtying memory should call in here once for each page
  * which was newly dirtied.  The function will periodically check the system's
index 4cbf8bb..f055c14 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -1297,8 +1297,7 @@ void __init kmem_cache_init(void)
                if (cache_cache.num)
                        break;
        }
-       if (!cache_cache.num)
-               BUG();
+       BUG_ON(!cache_cache.num);
        cache_cache.gfporder = order;
        cache_cache.colour = left_over / cache_cache.colour_off;
        cache_cache.slab_size = ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +
@@ -1974,8 +1973,7 @@ kmem_cache_create (const char *name, size_t size, size_t align,
         * Always checks flags, a caller might be expecting debug support which
         * isn't available.
         */
-       if (flags & ~CREATE_MASK)
-               BUG();
+       BUG_ON(flags & ~CREATE_MASK);
 
        /*
         * Check that size is in terms of words.  This is needed to avoid
@@ -2206,8 +2204,7 @@ static int __node_shrink(struct kmem_cache *cachep, int node)
 
                slabp = list_entry(l3->slabs_free.prev, struct slab, list);
 #if DEBUG
-               if (slabp->inuse)
-                       BUG();
+               BUG_ON(slabp->inuse);
 #endif
                list_del(&slabp->list);
 
@@ -2248,8 +2245,7 @@ static int __cache_shrink(struct kmem_cache *cachep)
  */
 int kmem_cache_shrink(struct kmem_cache *cachep)
 {
-       if (!cachep || in_interrupt())
-               BUG();
+       BUG_ON(!cachep || in_interrupt());
 
        return __cache_shrink(cachep);
 }
@@ -2277,8 +2273,7 @@ int kmem_cache_destroy(struct kmem_cache *cachep)
        int i;
        struct kmem_list3 *l3;
 
-       if (!cachep || in_interrupt())
-               BUG();
+       BUG_ON(!cachep || in_interrupt());
 
        /* Don't let CPUs to come and go */
        lock_cpu_hotplug();
@@ -2477,8 +2472,7 @@ static int cache_grow(struct kmem_cache *cachep, gfp_t flags, int nodeid)
         * Be lazy and only check for valid flags here,  keeping it out of the
         * critical path in kmem_cache_alloc().
         */
-       if (flags & ~(SLAB_DMA | SLAB_LEVEL_MASK | SLAB_NO_GROW))
-               BUG();
+       BUG_ON(flags & ~(SLAB_DMA | SLAB_LEVEL_MASK | SLAB_NO_GROW));
        if (flags & SLAB_NO_GROW)
                return 0;
 
index d7af296..e0e1583 100644 (file)
@@ -148,8 +148,7 @@ int add_to_swap(struct page * page, gfp_t gfp_mask)
        swp_entry_t entry;
        int err;
 
-       if (!PageLocked(page))
-               BUG();
+       BUG_ON(!PageLocked(page));
 
        for (;;) {
                entry = get_swap_page();
index 729eb3e..c0504f1 100644 (file)
@@ -321,8 +321,7 @@ void __vunmap(void *addr, int deallocate_pages)
                int i;
 
                for (i = 0; i < area->nr_pages; i++) {
-                       if (unlikely(!area->pages[i]))
-                               BUG();
+                       BUG_ON(!area->pages[i]);
                        __free_page(area->pages[i]);
                }
 
index 8fd37cd..d5d69fa 100644 (file)
@@ -476,8 +476,7 @@ asmlinkage long compat_sys_setsockopt(int fd, int level, int optname,
        int err;
        struct socket *sock;
 
-       /* SO_SET_REPLACE seems to be the same in all levels */
-       if (optname == IPT_SO_SET_REPLACE)
+       if (level == SOL_IPV6 && optname == IPT_SO_SET_REPLACE)
                return do_netfilter_replace(fd, level, optname,
                                            optval, optlen);
 
index e16d8b4..e2e4771 100644 (file)
@@ -116,7 +116,7 @@ error:
        return err;
 }
 
-static int ah_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
+static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        int ah_hlen;
        struct iphdr *iph;
index bf88c62..9d1881c 100644 (file)
@@ -133,7 +133,7 @@ error:
  * expensive, so we only support truncated data, which is the recommended
  * and common case.
  */
-static int esp_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
+static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        struct iphdr *iph;
        struct ip_esp_hdr *esph;
@@ -208,9 +208,6 @@ static int esp_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struc
                struct xfrm_encap_tmpl *encap = x->encap;
                struct udphdr *uh;
 
-               if (encap->encap_type != decap->decap_type)
-                       goto out;
-
                uh = (struct udphdr *)(iph + 1);
                encap_len = (void*)esph - (void*)uh;
 
index c95020f..0a1d86a 100644 (file)
@@ -81,8 +81,7 @@ out:
        return err;
 }
 
-static int ipcomp_input(struct xfrm_state *x,
-                        struct xfrm_decap_state *decap, struct sk_buff *skb)
+static int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        u8 nexthdr;
        int err = 0;
index 882b842..77855cc 100644 (file)
@@ -221,16 +221,6 @@ config IP_NF_MATCH_IPRANGE
 
          To compile it as a module, choose M here.  If unsure, say N.
 
-config IP_NF_MATCH_MULTIPORT
-       tristate "Multiple port match support"
-       depends on IP_NF_IPTABLES
-       help
-         Multiport matching allows you to match TCP or UDP packets based on
-         a series of source or destination ports: normally a rule can only
-         match a single range of ports.
-
-         To compile it as a module, choose M here.  If unsure, say N.
-
 config IP_NF_MATCH_TOS
        tristate "TOS match support"
        depends on IP_NF_IPTABLES
@@ -272,12 +262,12 @@ config IP_NF_MATCH_DSCP
 
          To compile it as a module, choose M here.  If unsure, say N.
 
-config IP_NF_MATCH_AH_ESP
-       tristate "AH/ESP match support"
+config IP_NF_MATCH_AH
+       tristate "AH match support"
        depends on IP_NF_IPTABLES
        help
-         These two match extensions (`ah' and `esp') allow you to match a
-         range of SPIs inside AH or ESP headers of IPSec packets.
+         This match extension allows you to match a range of SPIs
+         inside AH header of IPSec packets.
 
          To compile it as a module, choose M here.  If unsure, say N.
 
index f2cd9a6..461cb1e 100644 (file)
@@ -53,13 +53,12 @@ obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o
 # matches
 obj-$(CONFIG_IP_NF_MATCH_HASHLIMIT) += ipt_hashlimit.o
 obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o
-obj-$(CONFIG_IP_NF_MATCH_MULTIPORT) += ipt_multiport.o
 obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o
 obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o
 obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o
 obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o
 obj-$(CONFIG_IP_NF_MATCH_DSCP) += ipt_dscp.o
-obj-$(CONFIG_IP_NF_MATCH_AH_ESP) += ipt_ah.o ipt_esp.o
+obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o
 obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
 obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
 
index 9b6e19b..01bd7ca 100644 (file)
@@ -1658,7 +1658,7 @@ static void __exit ctnetlink_exit(void)
        printk("ctnetlink: unregistering from nfnetlink.\n");
 
 #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-       ip_conntrack_unregister_notifier(&ctnl_notifier_exp);
+       ip_conntrack_expect_unregister_notifier(&ctnl_notifier_exp);
        ip_conntrack_unregister_notifier(&ctnl_notifier);
 #endif
 
index 460fd90..d5b8cdd 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/icmp.h>
 #include <net/ip.h>
+#include <net/compat.h>
 #include <asm/uaccess.h>
 #include <linux/mutex.h>
 #include <linux/proc_fs.h>
@@ -799,17 +800,11 @@ get_counters(const struct xt_table_info *t,
        }
 }
 
-static int
-copy_entries_to_user(unsigned int total_size,
-                    struct ipt_table *table,
-                    void __user *userptr)
+static inline struct xt_counters * alloc_counters(struct ipt_table *table)
 {
-       unsigned int off, num, countersize;
-       struct ipt_entry *e;
+       unsigned int countersize;
        struct xt_counters *counters;
        struct xt_table_info *private = table->private;
-       int ret = 0;
-       void *loc_cpu_entry;
 
        /* We need atomic snapshot of counters: rest doesn't change
           (other than comefrom, which userspace doesn't care
@@ -818,13 +813,32 @@ copy_entries_to_user(unsigned int total_size,
        counters = vmalloc_node(countersize, numa_node_id());
 
        if (counters == NULL)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        /* First, sum counters... */
        write_lock_bh(&table->lock);
        get_counters(private, counters);
        write_unlock_bh(&table->lock);
 
+       return counters;
+}
+
+static int
+copy_entries_to_user(unsigned int total_size,
+                    struct ipt_table *table,
+                    void __user *userptr)
+{
+       unsigned int off, num;
+       struct ipt_entry *e;
+       struct xt_counters *counters;
+       struct xt_table_info *private = table->private;
+       int ret = 0;
+       void *loc_cpu_entry;
+
+       counters = alloc_counters(table);
+       if (IS_ERR(counters))
+               return PTR_ERR(counters);
+
        /* choose the copy that is on our node/cpu, ...
         * This choice is lazy (because current thread is
         * allowed to migrate to another cpu)
@@ -884,44 +898,899 @@ copy_entries_to_user(unsigned int total_size,
        return ret;
 }
 
+#ifdef CONFIG_COMPAT
+struct compat_delta {
+       struct compat_delta *next;
+       u_int16_t offset;
+       short delta;
+};
+
+static struct compat_delta *compat_offsets = NULL;
+
+static int compat_add_offset(u_int16_t offset, short delta)
+{
+       struct compat_delta *tmp;
+
+       tmp = kmalloc(sizeof(struct compat_delta), GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+       tmp->offset = offset;
+       tmp->delta = delta;
+       if (compat_offsets) {
+               tmp->next = compat_offsets->next;
+               compat_offsets->next = tmp;
+       } else {
+               compat_offsets = tmp;
+               tmp->next = NULL;
+       }
+       return 0;
+}
+
+static void compat_flush_offsets(void)
+{
+       struct compat_delta *tmp, *next;
+
+       if (compat_offsets) {
+               for(tmp = compat_offsets; tmp; tmp = next) {
+                       next = tmp->next;
+                       kfree(tmp);
+               }
+               compat_offsets = NULL;
+       }
+}
+
+static short compat_calc_jump(u_int16_t offset)
+{
+       struct compat_delta *tmp;
+       short delta;
+
+       for(tmp = compat_offsets, delta = 0; tmp; tmp = tmp->next)
+               if (tmp->offset < offset)
+                       delta += tmp->delta;
+       return delta;
+}
+
+struct compat_ipt_standard_target
+{
+       struct compat_xt_entry_target target;
+       compat_int_t verdict;
+};
+
+#define IPT_ST_OFFSET  (sizeof(struct ipt_standard_target) - \
+                               sizeof(struct compat_ipt_standard_target))
+
+struct compat_ipt_standard
+{
+       struct compat_ipt_entry entry;
+       struct compat_ipt_standard_target target;
+};
+
+static int compat_ipt_standard_fn(void *target,
+               void **dstptr, int *size, int convert)
+{
+       struct compat_ipt_standard_target compat_st, *pcompat_st;
+       struct ipt_standard_target st, *pst;
+       int ret;
+
+       ret = 0;
+       switch (convert) {
+               case COMPAT_TO_USER:
+                       pst = (struct ipt_standard_target *)target;
+                       memcpy(&compat_st.target, &pst->target,
+                                       sizeof(struct ipt_entry_target));
+                       compat_st.verdict = pst->verdict;
+                       if (compat_st.verdict > 0)
+                               compat_st.verdict -=
+                                       compat_calc_jump(compat_st.verdict);
+                       compat_st.target.u.user.target_size =
+                       sizeof(struct compat_ipt_standard_target);
+                       if (__copy_to_user(*dstptr, &compat_st,
+                               sizeof(struct compat_ipt_standard_target)))
+                               ret = -EFAULT;
+                       *size -= IPT_ST_OFFSET;
+                       *dstptr += sizeof(struct compat_ipt_standard_target);
+                       break;
+               case COMPAT_FROM_USER:
+                       pcompat_st =
+                               (struct compat_ipt_standard_target *)target;
+                       memcpy(&st.target, &pcompat_st->target,
+                                       sizeof(struct ipt_entry_target));
+                       st.verdict = pcompat_st->verdict;
+                       if (st.verdict > 0)
+                               st.verdict += compat_calc_jump(st.verdict);
+                       st.target.u.user.target_size =
+                       sizeof(struct ipt_standard_target);
+                       memcpy(*dstptr, &st,
+                                       sizeof(struct ipt_standard_target));
+                       *size += IPT_ST_OFFSET;
+                       *dstptr += sizeof(struct ipt_standard_target);
+                       break;
+               case COMPAT_CALC_SIZE:
+                       *size += IPT_ST_OFFSET;
+                       break;
+               default:
+                       ret = -ENOPROTOOPT;
+                       break;
+       }
+       return ret;
+}
+
+static inline int
+compat_calc_match(struct ipt_entry_match *m, int * size)
+{
+       if (m->u.kernel.match->compat)
+               m->u.kernel.match->compat(m, NULL, size, COMPAT_CALC_SIZE);
+       else
+               xt_compat_match(m, NULL, size, COMPAT_CALC_SIZE);
+       return 0;
+}
+
+static int compat_calc_entry(struct ipt_entry *e, struct xt_table_info *info,
+               void *base, struct xt_table_info *newinfo)
+{
+       struct ipt_entry_target *t;
+       u_int16_t entry_offset;
+       int off, i, ret;
+
+       off = 0;
+       entry_offset = (void *)e - base;
+       IPT_MATCH_ITERATE(e, compat_calc_match, &off);
+       t = ipt_get_target(e);
+       if (t->u.kernel.target->compat)
+               t->u.kernel.target->compat(t, NULL, &off, COMPAT_CALC_SIZE);
+       else
+               xt_compat_target(t, NULL, &off, COMPAT_CALC_SIZE);
+       newinfo->size -= off;
+       ret = compat_add_offset(entry_offset, off);
+       if (ret)
+               return ret;
+
+       for (i = 0; i< NF_IP_NUMHOOKS; i++) {
+               if (info->hook_entry[i] && (e < (struct ipt_entry *)
+                               (base + info->hook_entry[i])))
+                       newinfo->hook_entry[i] -= off;
+               if (info->underflow[i] && (e < (struct ipt_entry *)
+                               (base + info->underflow[i])))
+                       newinfo->underflow[i] -= off;
+       }
+       return 0;
+}
+
+static int compat_table_info(struct xt_table_info *info,
+               struct xt_table_info *newinfo)
+{
+       void *loc_cpu_entry;
+       int i;
+
+       if (!newinfo || !info)
+               return -EINVAL;
+
+       memset(newinfo, 0, sizeof(struct xt_table_info));
+       newinfo->size = info->size;
+       newinfo->number = info->number;
+       for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+               newinfo->hook_entry[i] = info->hook_entry[i];
+               newinfo->underflow[i] = info->underflow[i];
+       }
+       loc_cpu_entry = info->entries[raw_smp_processor_id()];
+       return IPT_ENTRY_ITERATE(loc_cpu_entry, info->size,
+                       compat_calc_entry, info, loc_cpu_entry, newinfo);
+}
+#endif
+
+static int get_info(void __user *user, int *len, int compat)
+{
+       char name[IPT_TABLE_MAXNAMELEN];
+       struct ipt_table *t;
+       int ret;
+
+       if (*len != sizeof(struct ipt_getinfo)) {
+               duprintf("length %u != %u\n", *len,
+                       (unsigned int)sizeof(struct ipt_getinfo));
+               return -EINVAL;
+       }
+
+       if (copy_from_user(name, user, sizeof(name)) != 0)
+               return -EFAULT;
+
+       name[IPT_TABLE_MAXNAMELEN-1] = '\0';
+#ifdef CONFIG_COMPAT
+       if (compat)
+               xt_compat_lock(AF_INET);
+#endif
+       t = try_then_request_module(xt_find_table_lock(AF_INET, name),
+                       "iptable_%s", name);
+       if (t && !IS_ERR(t)) {
+               struct ipt_getinfo info;
+               struct xt_table_info *private = t->private;
+
+#ifdef CONFIG_COMPAT
+               if (compat) {
+                       struct xt_table_info tmp;
+                       ret = compat_table_info(private, &tmp);
+                       compat_flush_offsets();
+                       private =  &tmp;
+               }
+#endif
+               info.valid_hooks = t->valid_hooks;
+               memcpy(info.hook_entry, private->hook_entry,
+                               sizeof(info.hook_entry));
+               memcpy(info.underflow, private->underflow,
+                               sizeof(info.underflow));
+               info.num_entries = private->number;
+               info.size = private->size;
+               strcpy(info.name, name);
+
+               if (copy_to_user(user, &info, *len) != 0)
+                       ret = -EFAULT;
+               else
+                       ret = 0;
+
+               xt_table_unlock(t);
+               module_put(t->me);
+       } else
+               ret = t ? PTR_ERR(t) : -ENOENT;
+#ifdef CONFIG_COMPAT
+       if (compat)
+               xt_compat_unlock(AF_INET);
+#endif
+       return ret;
+}
+
+static int
+get_entries(struct ipt_get_entries __user *uptr, int *len)
+{
+       int ret;
+       struct ipt_get_entries get;
+       struct ipt_table *t;
+
+       if (*len < sizeof(get)) {
+               duprintf("get_entries: %u < %d\n", *len,
+                               (unsigned int)sizeof(get));
+               return -EINVAL;
+       }
+       if (copy_from_user(&get, uptr, sizeof(get)) != 0)
+               return -EFAULT;
+       if (*len != sizeof(struct ipt_get_entries) + get.size) {
+               duprintf("get_entries: %u != %u\n", *len,
+                               (unsigned int)(sizeof(struct ipt_get_entries) +
+                               get.size));
+               return -EINVAL;
+       }
+
+       t = xt_find_table_lock(AF_INET, get.name);
+       if (t && !IS_ERR(t)) {
+               struct xt_table_info *private = t->private;
+               duprintf("t->private->number = %u\n",
+                        private->number);
+               if (get.size == private->size)
+                       ret = copy_entries_to_user(private->size,
+                                                  t, uptr->entrytable);
+               else {
+                       duprintf("get_entries: I've got %u not %u!\n",
+                                private->size,
+                                get.size);
+                       ret = -EINVAL;
+               }
+               module_put(t->me);
+               xt_table_unlock(t);
+       } else
+               ret = t ? PTR_ERR(t) : -ENOENT;
+
+       return ret;
+}
+
+static int
+__do_replace(const char *name, unsigned int valid_hooks,
+               struct xt_table_info *newinfo, unsigned int num_counters,
+               void __user *counters_ptr)
+{
+       int ret;
+       struct ipt_table *t;
+       struct xt_table_info *oldinfo;
+       struct xt_counters *counters;
+       void *loc_cpu_old_entry;
+
+       ret = 0;
+       counters = vmalloc(num_counters * sizeof(struct xt_counters));
+       if (!counters) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       t = try_then_request_module(xt_find_table_lock(AF_INET, name),
+                                   "iptable_%s", name);
+       if (!t || IS_ERR(t)) {
+               ret = t ? PTR_ERR(t) : -ENOENT;
+               goto free_newinfo_counters_untrans;
+       }
+
+       /* You lied! */
+       if (valid_hooks != t->valid_hooks) {
+               duprintf("Valid hook crap: %08X vs %08X\n",
+                        valid_hooks, t->valid_hooks);
+               ret = -EINVAL;
+               goto put_module;
+       }
+
+       oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
+       if (!oldinfo)
+               goto put_module;
+
+       /* Update module usage count based on number of rules */
+       duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
+               oldinfo->number, oldinfo->initial_entries, newinfo->number);
+       if ((oldinfo->number > oldinfo->initial_entries) ||
+           (newinfo->number <= oldinfo->initial_entries))
+               module_put(t->me);
+       if ((oldinfo->number > oldinfo->initial_entries) &&
+           (newinfo->number <= oldinfo->initial_entries))
+               module_put(t->me);
+
+       /* Get the old counters. */
+       get_counters(oldinfo, counters);
+       /* Decrease module usage counts and free resource */
+       loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
+       IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
+       xt_free_table_info(oldinfo);
+       if (copy_to_user(counters_ptr, counters,
+                        sizeof(struct xt_counters) * num_counters) != 0)
+               ret = -EFAULT;
+       vfree(counters);
+       xt_table_unlock(t);
+       return ret;
+
+ put_module:
+       module_put(t->me);
+       xt_table_unlock(t);
+ free_newinfo_counters_untrans:
+       vfree(counters);
+ out:
+       return ret;
+}
+
+static int
+do_replace(void __user *user, unsigned int len)
+{
+       int ret;
+       struct ipt_replace tmp;
+       struct xt_table_info *newinfo;
+       void *loc_cpu_entry;
+
+       if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+               return -EFAULT;
+
+       /* Hack: Causes ipchains to give correct error msg --RR */
+       if (len != sizeof(tmp) + tmp.size)
+               return -ENOPROTOOPT;
+
+       /* overflow check */
+       if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
+                       SMP_CACHE_BYTES)
+               return -ENOMEM;
+       if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
+               return -ENOMEM;
+
+       newinfo = xt_alloc_table_info(tmp.size);
+       if (!newinfo)
+               return -ENOMEM;
+
+       /* choose the copy that is our node/cpu */
+       loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
+       if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
+                          tmp.size) != 0) {
+               ret = -EFAULT;
+               goto free_newinfo;
+       }
+
+       ret = translate_table(tmp.name, tmp.valid_hooks,
+                             newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
+                             tmp.hook_entry, tmp.underflow);
+       if (ret != 0)
+               goto free_newinfo;
+
+       duprintf("ip_tables: Translated table\n");
+
+       ret = __do_replace(tmp.name, tmp.valid_hooks,
+                             newinfo, tmp.num_counters,
+                             tmp.counters);
+       if (ret)
+               goto free_newinfo_untrans;
+       return 0;
+
+ free_newinfo_untrans:
+       IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
+ free_newinfo:
+       xt_free_table_info(newinfo);
+       return ret;
+}
+
+/* We're lazy, and add to the first CPU; overflow works its fey magic
+ * and everything is OK. */
+static inline int
+add_counter_to_entry(struct ipt_entry *e,
+                    const struct xt_counters addme[],
+                    unsigned int *i)
+{
+#if 0
+       duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
+                *i,
+                (long unsigned int)e->counters.pcnt,
+                (long unsigned int)e->counters.bcnt,
+                (long unsigned int)addme[*i].pcnt,
+                (long unsigned int)addme[*i].bcnt);
+#endif
+
+       ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
+
+       (*i)++;
+       return 0;
+}
+
+static int
+do_add_counters(void __user *user, unsigned int len, int compat)
+{
+       unsigned int i;
+       struct xt_counters_info tmp;
+       struct xt_counters *paddc;
+       unsigned int num_counters;
+       char *name;
+       int size;
+       void *ptmp;
+       struct ipt_table *t;
+       struct xt_table_info *private;
+       int ret = 0;
+       void *loc_cpu_entry;
+#ifdef CONFIG_COMPAT
+       struct compat_xt_counters_info compat_tmp;
+
+       if (compat) {
+               ptmp = &compat_tmp;
+               size = sizeof(struct compat_xt_counters_info);
+       } else
+#endif
+       {
+               ptmp = &tmp;
+               size = sizeof(struct xt_counters_info);
+       }
+
+       if (copy_from_user(ptmp, user, size) != 0)
+               return -EFAULT;
+
+#ifdef CONFIG_COMPAT
+       if (compat) {
+               num_counters = compat_tmp.num_counters;
+               name = compat_tmp.name;
+       } else
+#endif
+       {
+               num_counters = tmp.num_counters;
+               name = tmp.name;
+       }
+
+       if (len != size + num_counters * sizeof(struct xt_counters))
+               return -EINVAL;
+
+       paddc = vmalloc_node(len - size, numa_node_id());
+       if (!paddc)
+               return -ENOMEM;
+
+       if (copy_from_user(paddc, user + size, len - size) != 0) {
+               ret = -EFAULT;
+               goto free;
+       }
+
+       t = xt_find_table_lock(AF_INET, name);
+       if (!t || IS_ERR(t)) {
+               ret = t ? PTR_ERR(t) : -ENOENT;
+               goto free;
+       }
+
+       write_lock_bh(&t->lock);
+       private = t->private;
+       if (private->number != num_counters) {
+               ret = -EINVAL;
+               goto unlock_up_free;
+       }
+
+       i = 0;
+       /* Choose the copy that is on our node */
+       loc_cpu_entry = private->entries[raw_smp_processor_id()];
+       IPT_ENTRY_ITERATE(loc_cpu_entry,
+                         private->size,
+                         add_counter_to_entry,
+                         paddc,
+                         &i);
+ unlock_up_free:
+       write_unlock_bh(&t->lock);
+       xt_table_unlock(t);
+       module_put(t->me);
+ free:
+       vfree(paddc);
+
+       return ret;
+}
+
+#ifdef CONFIG_COMPAT
+struct compat_ipt_replace {
+       char                    name[IPT_TABLE_MAXNAMELEN];
+       u32                     valid_hooks;
+       u32                     num_entries;
+       u32                     size;
+       u32                     hook_entry[NF_IP_NUMHOOKS];
+       u32                     underflow[NF_IP_NUMHOOKS];
+       u32                     num_counters;
+       compat_uptr_t           counters;       /* struct ipt_counters * */
+       struct compat_ipt_entry entries[0];
+};
+
+static inline int compat_copy_match_to_user(struct ipt_entry_match *m,
+               void __user **dstptr, compat_uint_t *size)
+{
+       if (m->u.kernel.match->compat)
+               return m->u.kernel.match->compat(m, dstptr, size,
+                               COMPAT_TO_USER);
+       else
+               return xt_compat_match(m, dstptr, size, COMPAT_TO_USER);
+}
+
+static int compat_copy_entry_to_user(struct ipt_entry *e,
+               void __user **dstptr, compat_uint_t *size)
+{
+       struct ipt_entry_target __user *t;
+       struct compat_ipt_entry __user *ce;
+       u_int16_t target_offset, next_offset;
+       compat_uint_t origsize;
+       int ret;
+
+       ret = -EFAULT;
+       origsize = *size;
+       ce = (struct compat_ipt_entry __user *)*dstptr;
+       if (__copy_to_user(ce, e, sizeof(struct ipt_entry)))
+               goto out;
+
+       *dstptr += sizeof(struct compat_ipt_entry);
+       ret = IPT_MATCH_ITERATE(e, compat_copy_match_to_user, dstptr, size);
+       target_offset = e->target_offset - (origsize - *size);
+       if (ret)
+               goto out;
+       t = ipt_get_target(e);
+       if (t->u.kernel.target->compat)
+               ret = t->u.kernel.target->compat(t, dstptr, size,
+                               COMPAT_TO_USER);
+       else
+               ret = xt_compat_target(t, dstptr, size, COMPAT_TO_USER);
+       if (ret)
+               goto out;
+       ret = -EFAULT;
+       next_offset = e->next_offset - (origsize - *size);
+       if (__put_user(target_offset, &ce->target_offset))
+               goto out;
+       if (__put_user(next_offset, &ce->next_offset))
+               goto out;
+       return 0;
+out:
+       return ret;
+}
+
+static inline int
+compat_check_calc_match(struct ipt_entry_match *m,
+           const char *name,
+           const struct ipt_ip *ip,
+           unsigned int hookmask,
+           int *size, int *i)
+{
+       struct ipt_match *match;
+
+       match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
+                                                  m->u.user.revision),
+                                       "ipt_%s", m->u.user.name);
+       if (IS_ERR(match) || !match) {
+               duprintf("compat_check_calc_match: `%s' not found\n",
+                               m->u.user.name);
+               return match ? PTR_ERR(match) : -ENOENT;
+       }
+       m->u.kernel.match = match;
+
+       if (m->u.kernel.match->compat)
+               m->u.kernel.match->compat(m, NULL, size, COMPAT_CALC_SIZE);
+       else
+               xt_compat_match(m, NULL, size, COMPAT_CALC_SIZE);
+
+       (*i)++;
+       return 0;
+}
+
+static inline int
+check_compat_entry_size_and_hooks(struct ipt_entry *e,
+                          struct xt_table_info *newinfo,
+                          unsigned int *size,
+                          unsigned char *base,
+                          unsigned char *limit,
+                          unsigned int *hook_entries,
+                          unsigned int *underflows,
+                          unsigned int *i,
+                          const char *name)
+{
+       struct ipt_entry_target *t;
+       struct ipt_target *target;
+       u_int16_t entry_offset;
+       int ret, off, h, j;
+
+       duprintf("check_compat_entry_size_and_hooks %p\n", e);
+       if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0
+           || (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
+               duprintf("Bad offset %p, limit = %p\n", e, limit);
+               return -EINVAL;
+       }
+
+       if (e->next_offset < sizeof(struct compat_ipt_entry) +
+                       sizeof(struct compat_xt_entry_target)) {
+               duprintf("checking: element %p size %u\n",
+                        e, e->next_offset);
+               return -EINVAL;
+       }
+
+       if (!ip_checkentry(&e->ip)) {
+               duprintf("ip_tables: ip check failed %p %s.\n", e, name);
+               return -EINVAL;
+       }
+
+       off = 0;
+       entry_offset = (void *)e - (void *)base;
+       j = 0;
+       ret = IPT_MATCH_ITERATE(e, compat_check_calc_match, name, &e->ip,
+                       e->comefrom, &off, &j);
+       if (ret != 0)
+               goto out;
+
+       t = ipt_get_target(e);
+       target = try_then_request_module(xt_find_target(AF_INET,
+                                                    t->u.user.name,
+                                                    t->u.user.revision),
+                                        "ipt_%s", t->u.user.name);
+       if (IS_ERR(target) || !target) {
+               duprintf("check_entry: `%s' not found\n", t->u.user.name);
+               ret = target ? PTR_ERR(target) : -ENOENT;
+               goto out;
+       }
+       t->u.kernel.target = target;
+
+       if (t->u.kernel.target->compat)
+               t->u.kernel.target->compat(t, NULL, &off, COMPAT_CALC_SIZE);
+       else
+               xt_compat_target(t, NULL, &off, COMPAT_CALC_SIZE);
+       *size += off;
+       ret = compat_add_offset(entry_offset, off);
+       if (ret)
+               goto out;
+
+       /* Check hooks & underflows */
+       for (h = 0; h < NF_IP_NUMHOOKS; h++) {
+               if ((unsigned char *)e - base == hook_entries[h])
+                       newinfo->hook_entry[h] = hook_entries[h];
+               if ((unsigned char *)e - base == underflows[h])
+                       newinfo->underflow[h] = underflows[h];
+       }
+
+       /* Clear counters and comefrom */
+       e->counters = ((struct ipt_counters) { 0, 0 });
+       e->comefrom = 0;
+
+       (*i)++;
+       return 0;
+out:
+       IPT_MATCH_ITERATE(e, cleanup_match, &j);
+       return ret;
+}
+
+static inline int compat_copy_match_from_user(struct ipt_entry_match *m,
+       void **dstptr, compat_uint_t *size, const char *name,
+       const struct ipt_ip *ip, unsigned int hookmask)
+{
+       struct ipt_entry_match *dm;
+       struct ipt_match *match;
+       int ret;
+
+       dm = (struct ipt_entry_match *)*dstptr;
+       match = m->u.kernel.match;
+       if (match->compat)
+               match->compat(m, dstptr, size, COMPAT_FROM_USER);
+       else
+               xt_compat_match(m, dstptr, size, COMPAT_FROM_USER);
+
+       ret = xt_check_match(match, AF_INET, dm->u.match_size - sizeof(*dm),
+                            name, hookmask, ip->proto,
+                            ip->invflags & IPT_INV_PROTO);
+       if (ret)
+               return ret;
+
+       if (m->u.kernel.match->checkentry
+           && !m->u.kernel.match->checkentry(name, ip, match, dm->data,
+                                             dm->u.match_size - sizeof(*dm),
+                                             hookmask)) {
+               duprintf("ip_tables: check failed for `%s'.\n",
+                        m->u.kernel.match->name);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr,
+       unsigned int *size, const char *name,
+       struct xt_table_info *newinfo, unsigned char *base)
+{
+       struct ipt_entry_target *t;
+       struct ipt_target *target;
+       struct ipt_entry *de;
+       unsigned int origsize;
+       int ret, h;
+
+       ret = 0;
+       origsize = *size;
+       de = (struct ipt_entry *)*dstptr;
+       memcpy(de, e, sizeof(struct ipt_entry));
+
+       *dstptr += sizeof(struct compat_ipt_entry);
+       ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size,
+                       name, &de->ip, de->comefrom);
+       if (ret)
+               goto out;
+       de->target_offset = e->target_offset - (origsize - *size);
+       t = ipt_get_target(e);
+       target = t->u.kernel.target;
+       if (target->compat)
+               target->compat(t, dstptr, size, COMPAT_FROM_USER);
+       else
+               xt_compat_target(t, dstptr, size, COMPAT_FROM_USER);
+
+       de->next_offset = e->next_offset - (origsize - *size);
+       for (h = 0; h < NF_IP_NUMHOOKS; h++) {
+               if ((unsigned char *)de - base < newinfo->hook_entry[h])
+                       newinfo->hook_entry[h] -= origsize - *size;
+               if ((unsigned char *)de - base < newinfo->underflow[h])
+                       newinfo->underflow[h] -= origsize - *size;
+       }
+
+       t = ipt_get_target(de);
+       target = t->u.kernel.target;
+       ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t),
+                             name, e->comefrom, e->ip.proto,
+                             e->ip.invflags & IPT_INV_PROTO);
+       if (ret)
+               goto out;
+
+       ret = -EINVAL;
+       if (t->u.kernel.target == &ipt_standard_target) {
+               if (!standard_check(t, *size))
+                       goto out;
+       } else if (t->u.kernel.target->checkentry
+                  && !t->u.kernel.target->checkentry(name, de, target,
+                               t->data, t->u.target_size - sizeof(*t),
+                               de->comefrom)) {
+               duprintf("ip_tables: compat: check failed for `%s'.\n",
+                        t->u.kernel.target->name);
+               goto out;
+       }
+       ret = 0;
+out:
+       return ret;
+}
+
 static int
-get_entries(const struct ipt_get_entries *entries,
-           struct ipt_get_entries __user *uptr)
+translate_compat_table(const char *name,
+               unsigned int valid_hooks,
+               struct xt_table_info **pinfo,
+               void **pentry0,
+               unsigned int total_size,
+               unsigned int number,
+               unsigned int *hook_entries,
+               unsigned int *underflows)
 {
+       unsigned int i;
+       struct xt_table_info *newinfo, *info;
+       void *pos, *entry0, *entry1;
+       unsigned int size;
        int ret;
-       struct ipt_table *t;
 
-       t = xt_find_table_lock(AF_INET, entries->name);
-       if (t && !IS_ERR(t)) {
-               struct xt_table_info *private = t->private;
-               duprintf("t->private->number = %u\n",
-                        private->number);
-               if (entries->size == private->size)
-                       ret = copy_entries_to_user(private->size,
-                                                  t, uptr->entrytable);
-               else {
-                       duprintf("get_entries: I've got %u not %u!\n",
-                                private->size,
-                                entries->size);
-                       ret = -EINVAL;
+       info = *pinfo;
+       entry0 = *pentry0;
+       size = total_size;
+       info->number = number;
+
+       /* Init all hooks to impossible value. */
+       for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+               info->hook_entry[i] = 0xFFFFFFFF;
+               info->underflow[i] = 0xFFFFFFFF;
+       }
+
+       duprintf("translate_compat_table: size %u\n", info->size);
+       i = 0;
+       xt_compat_lock(AF_INET);
+       /* Walk through entries, checking offsets. */
+       ret = IPT_ENTRY_ITERATE(entry0, total_size,
+                               check_compat_entry_size_and_hooks,
+                               info, &size, entry0,
+                               entry0 + total_size,
+                               hook_entries, underflows, &i, name);
+       if (ret != 0)
+               goto out_unlock;
+
+       ret = -EINVAL;
+       if (i != number) {
+               duprintf("translate_compat_table: %u not %u entries\n",
+                        i, number);
+               goto out_unlock;
+       }
+
+       /* Check hooks all assigned */
+       for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+               /* Only hooks which are valid */
+               if (!(valid_hooks & (1 << i)))
+                       continue;
+               if (info->hook_entry[i] == 0xFFFFFFFF) {
+                       duprintf("Invalid hook entry %u %u\n",
+                                i, hook_entries[i]);
+                       goto out_unlock;
                }
-               module_put(t->me);
-               xt_table_unlock(t);
-       } else
-               ret = t ? PTR_ERR(t) : -ENOENT;
+               if (info->underflow[i] == 0xFFFFFFFF) {
+                       duprintf("Invalid underflow %u %u\n",
+                                i, underflows[i]);
+                       goto out_unlock;
+               }
+       }
+
+       ret = -ENOMEM;
+       newinfo = xt_alloc_table_info(size);
+       if (!newinfo)
+               goto out_unlock;
+
+       newinfo->number = number;
+       for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+               newinfo->hook_entry[i] = info->hook_entry[i];
+               newinfo->underflow[i] = info->underflow[i];
+       }
+       entry1 = newinfo->entries[raw_smp_processor_id()];
+       pos = entry1;
+       size =  total_size;
+       ret = IPT_ENTRY_ITERATE(entry0, total_size,
+                       compat_copy_entry_from_user, &pos, &size,
+                       name, newinfo, entry1);
+       compat_flush_offsets();
+       xt_compat_unlock(AF_INET);
+       if (ret)
+               goto free_newinfo;
+
+       ret = -ELOOP;
+       if (!mark_source_chains(newinfo, valid_hooks, entry1))
+               goto free_newinfo;
+
+       /* And one copy for every other CPU */
+       for_each_cpu(i)
+               if (newinfo->entries[i] && newinfo->entries[i] != entry1)
+                       memcpy(newinfo->entries[i], entry1, newinfo->size);
+
+       *pinfo = newinfo;
+       *pentry0 = entry1;
+       xt_free_table_info(info);
+       return 0;
 
+free_newinfo:
+       xt_free_table_info(newinfo);
+out:
        return ret;
+out_unlock:
+       xt_compat_unlock(AF_INET);
+       goto out;
 }
 
 static int
-do_replace(void __user *user, unsigned int len)
+compat_do_replace(void __user *user, unsigned int len)
 {
        int ret;
-       struct ipt_replace tmp;
-       struct ipt_table *t;
-       struct xt_table_info *newinfo, *oldinfo;
-       struct xt_counters *counters;
-       void *loc_cpu_entry, *loc_cpu_old_entry;
+       struct compat_ipt_replace tmp;
+       struct xt_table_info *newinfo;
+       void *loc_cpu_entry;
 
        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
                return -EFAULT;
@@ -949,151 +1818,201 @@ do_replace(void __user *user, unsigned int len)
                goto free_newinfo;
        }
 
-       counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
-       if (!counters) {
-               ret = -ENOMEM;
+       ret = translate_compat_table(tmp.name, tmp.valid_hooks,
+                             &newinfo, &loc_cpu_entry, tmp.size,
+                             tmp.num_entries, tmp.hook_entry, tmp.underflow);
+       if (ret != 0)
                goto free_newinfo;
-       }
 
-       ret = translate_table(tmp.name, tmp.valid_hooks,
-                             newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
-                             tmp.hook_entry, tmp.underflow);
-       if (ret != 0)
-               goto free_newinfo_counters;
+       duprintf("compat_do_replace: Translated table\n");
 
-       duprintf("ip_tables: Translated table\n");
+       ret = __do_replace(tmp.name, tmp.valid_hooks,
+                             newinfo, tmp.num_counters,
+                             compat_ptr(tmp.counters));
+       if (ret)
+               goto free_newinfo_untrans;
+       return 0;
 
-       t = try_then_request_module(xt_find_table_lock(AF_INET, tmp.name),
-                                   "iptable_%s", tmp.name);
-       if (!t || IS_ERR(t)) {
-               ret = t ? PTR_ERR(t) : -ENOENT;
-               goto free_newinfo_counters_untrans;
-       }
+ free_newinfo_untrans:
+       IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
+ free_newinfo:
+       xt_free_table_info(newinfo);
+       return ret;
+}
 
-       /* You lied! */
-       if (tmp.valid_hooks != t->valid_hooks) {
-               duprintf("Valid hook crap: %08X vs %08X\n",
-                        tmp.valid_hooks, t->valid_hooks);
-               ret = -EINVAL;
-               goto put_module;
-       }
+static int
+compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user,
+               unsigned int len)
+{
+       int ret;
 
-       oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
-       if (!oldinfo)
-               goto put_module;
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
 
-       /* Update module usage count based on number of rules */
-       duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
-               oldinfo->number, oldinfo->initial_entries, newinfo->number);
-       if ((oldinfo->number > oldinfo->initial_entries) || 
-           (newinfo->number <= oldinfo->initial_entries)) 
-               module_put(t->me);
-       if ((oldinfo->number > oldinfo->initial_entries) &&
-           (newinfo->number <= oldinfo->initial_entries))
-               module_put(t->me);
+       switch (cmd) {
+       case IPT_SO_SET_REPLACE:
+               ret = compat_do_replace(user, len);
+               break;
 
-       /* Get the old counters. */
-       get_counters(oldinfo, counters);
-       /* Decrease module usage counts and free resource */
-       loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
-       IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
-       xt_free_table_info(oldinfo);
-       if (copy_to_user(tmp.counters, counters,
-                        sizeof(struct xt_counters) * tmp.num_counters) != 0)
-               ret = -EFAULT;
-       vfree(counters);
-       xt_table_unlock(t);
-       return ret;
+       case IPT_SO_SET_ADD_COUNTERS:
+               ret = do_add_counters(user, len, 1);
+               break;
+
+       default:
+               duprintf("do_ipt_set_ctl:  unknown request %i\n", cmd);
+               ret = -EINVAL;
+       }
 
- put_module:
-       module_put(t->me);
-       xt_table_unlock(t);
- free_newinfo_counters_untrans:
-       IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
- free_newinfo_counters:
-       vfree(counters);
- free_newinfo:
-       xt_free_table_info(newinfo);
        return ret;
 }
 
-/* We're lazy, and add to the first CPU; overflow works its fey magic
- * and everything is OK. */
-static inline int
-add_counter_to_entry(struct ipt_entry *e,
-                    const struct xt_counters addme[],
-                    unsigned int *i)
+struct compat_ipt_get_entries
 {
-#if 0
-       duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
-                *i,
-                (long unsigned int)e->counters.pcnt,
-                (long unsigned int)e->counters.bcnt,
-                (long unsigned int)addme[*i].pcnt,
-                (long unsigned int)addme[*i].bcnt);
-#endif
+       char name[IPT_TABLE_MAXNAMELEN];
+       compat_uint_t size;
+       struct compat_ipt_entry entrytable[0];
+};
 
-       ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
+static int compat_copy_entries_to_user(unsigned int total_size,
+                    struct ipt_table *table, void __user *userptr)
+{
+       unsigned int off, num;
+       struct compat_ipt_entry e;
+       struct xt_counters *counters;
+       struct xt_table_info *private = table->private;
+       void __user *pos;
+       unsigned int size;
+       int ret = 0;
+       void *loc_cpu_entry;
 
-       (*i)++;
-       return 0;
+       counters = alloc_counters(table);
+       if (IS_ERR(counters))
+               return PTR_ERR(counters);
+
+       /* choose the copy that is on our node/cpu, ...
+        * This choice is lazy (because current thread is
+        * allowed to migrate to another cpu)
+        */
+       loc_cpu_entry = private->entries[raw_smp_processor_id()];
+       pos = userptr;
+       size = total_size;
+       ret = IPT_ENTRY_ITERATE(loc_cpu_entry, total_size,
+                       compat_copy_entry_to_user, &pos, &size);
+       if (ret)
+               goto free_counters;
+
+       /* ... then go back and fix counters and names */
+       for (off = 0, num = 0; off < size; off += e.next_offset, num++) {
+               unsigned int i;
+               struct ipt_entry_match m;
+               struct ipt_entry_target t;
+
+               ret = -EFAULT;
+               if (copy_from_user(&e, userptr + off,
+                                       sizeof(struct compat_ipt_entry)))
+                       goto free_counters;
+               if (copy_to_user(userptr + off +
+                       offsetof(struct compat_ipt_entry, counters),
+                        &counters[num], sizeof(counters[num])))
+                       goto free_counters;
+
+               for (i = sizeof(struct compat_ipt_entry);
+                               i < e.target_offset; i += m.u.match_size) {
+                       if (copy_from_user(&m, userptr + off + i,
+                                       sizeof(struct ipt_entry_match)))
+                               goto free_counters;
+                       if (copy_to_user(userptr + off + i +
+                               offsetof(struct ipt_entry_match, u.user.name),
+                               m.u.kernel.match->name,
+                               strlen(m.u.kernel.match->name) + 1))
+                               goto free_counters;
+               }
+
+               if (copy_from_user(&t, userptr + off + e.target_offset,
+                                       sizeof(struct ipt_entry_target)))
+                       goto free_counters;
+               if (copy_to_user(userptr + off + e.target_offset +
+                       offsetof(struct ipt_entry_target, u.user.name),
+                       t.u.kernel.target->name,
+                       strlen(t.u.kernel.target->name) + 1))
+                       goto free_counters;
+       }
+       ret = 0;
+free_counters:
+       vfree(counters);
+       return ret;
 }
 
 static int
-do_add_counters(void __user *user, unsigned int len)
+compat_get_entries(struct compat_ipt_get_entries __user *uptr, int *len)
 {
-       unsigned int i;
-       struct xt_counters_info tmp, *paddc;
+       int ret;
+       struct compat_ipt_get_entries get;
        struct ipt_table *t;
-       struct xt_table_info *private;
-       int ret = 0;
-       void *loc_cpu_entry;
 
-       if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
-               return -EFAULT;
 
-       if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
+       if (*len < sizeof(get)) {
+               duprintf("compat_get_entries: %u < %u\n",
+                               *len, (unsigned int)sizeof(get));
                return -EINVAL;
+       }
 
-       paddc = vmalloc_node(len, numa_node_id());
-       if (!paddc)
-               return -ENOMEM;
+       if (copy_from_user(&get, uptr, sizeof(get)) != 0)
+               return -EFAULT;
 
-       if (copy_from_user(paddc, user, len) != 0) {
-               ret = -EFAULT;
-               goto free;
+       if (*len != sizeof(struct compat_ipt_get_entries) + get.size) {
+               duprintf("compat_get_entries: %u != %u\n", *len,
+                       (unsigned int)(sizeof(struct compat_ipt_get_entries) +
+                       get.size));
+               return -EINVAL;
        }
 
-       t = xt_find_table_lock(AF_INET, tmp.name);
-       if (!t || IS_ERR(t)) {
+       xt_compat_lock(AF_INET);
+       t = xt_find_table_lock(AF_INET, get.name);
+       if (t && !IS_ERR(t)) {
+               struct xt_table_info *private = t->private;
+               struct xt_table_info info;
+               duprintf("t->private->number = %u\n",
+                        private->number);
+               ret = compat_table_info(private, &info);
+               if (!ret && get.size == info.size) {
+                       ret = compat_copy_entries_to_user(private->size,
+                                                  t, uptr->entrytable);
+               } else if (!ret) {
+                       duprintf("compat_get_entries: I've got %u not %u!\n",
+                                private->size,
+                                get.size);
+                       ret = -EINVAL;
+               }
+               compat_flush_offsets();
+               module_put(t->me);
+               xt_table_unlock(t);
+       } else
                ret = t ? PTR_ERR(t) : -ENOENT;
-               goto free;
-       }
 
-       write_lock_bh(&t->lock);
-       private = t->private;
-       if (private->number != paddc->num_counters) {
-               ret = -EINVAL;
-               goto unlock_up_free;
-       }
+       xt_compat_unlock(AF_INET);
+       return ret;
+}
 
-       i = 0;
-       /* Choose the copy that is on our node */
-       loc_cpu_entry = private->entries[raw_smp_processor_id()];
-       IPT_ENTRY_ITERATE(loc_cpu_entry,
-                         private->size,
-                         add_counter_to_entry,
-                         paddc->counters,
-                         &i);
- unlock_up_free:
-       write_unlock_bh(&t->lock);
-       xt_table_unlock(t);
-       module_put(t->me);
- free:
-       vfree(paddc);
+static int
+compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
+{
+       int ret;
 
+       switch (cmd) {
+       case IPT_SO_GET_INFO:
+               ret = get_info(user, len, 1);
+               break;
+       case IPT_SO_GET_ENTRIES:
+               ret = compat_get_entries(user, len);
+               break;
+       default:
+               duprintf("compat_do_ipt_get_ctl: unknown request %i\n", cmd);
+               ret = -EINVAL;
+       }
        return ret;
 }
+#endif
 
 static int
 do_ipt_set_ctl(struct sock *sk,        int cmd, void __user *user, unsigned int len)
@@ -1109,7 +2028,7 @@ do_ipt_set_ctl(struct sock *sk,   int cmd, void __user *user, unsigned int len)
                break;
 
        case IPT_SO_SET_ADD_COUNTERS:
-               ret = do_add_counters(user, len);
+               ret = do_add_counters(user, len, 0);
                break;
 
        default:
@@ -1129,65 +2048,13 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
                return -EPERM;
 
        switch (cmd) {
-       case IPT_SO_GET_INFO: {
-               char name[IPT_TABLE_MAXNAMELEN];
-               struct ipt_table *t;
-
-               if (*len != sizeof(struct ipt_getinfo)) {
-                       duprintf("length %u != %u\n", *len,
-                                sizeof(struct ipt_getinfo));
-                       ret = -EINVAL;
-                       break;
-               }
-
-               if (copy_from_user(name, user, sizeof(name)) != 0) {
-                       ret = -EFAULT;
-                       break;
-               }
-               name[IPT_TABLE_MAXNAMELEN-1] = '\0';
-
-               t = try_then_request_module(xt_find_table_lock(AF_INET, name),
-                                           "iptable_%s", name);
-               if (t && !IS_ERR(t)) {
-                       struct ipt_getinfo info;
-                       struct xt_table_info *private = t->private;
-
-                       info.valid_hooks = t->valid_hooks;
-                       memcpy(info.hook_entry, private->hook_entry,
-                              sizeof(info.hook_entry));
-                       memcpy(info.underflow, private->underflow,
-                              sizeof(info.underflow));
-                       info.num_entries = private->number;
-                       info.size = private->size;
-                       memcpy(info.name, name, sizeof(info.name));
-
-                       if (copy_to_user(user, &info, *len) != 0)
-                               ret = -EFAULT;
-                       else
-                               ret = 0;
-                       xt_table_unlock(t);
-                       module_put(t->me);
-               } else
-                       ret = t ? PTR_ERR(t) : -ENOENT;
-       }
-       break;
-
-       case IPT_SO_GET_ENTRIES: {
-               struct ipt_get_entries get;
+       case IPT_SO_GET_INFO:
+               ret = get_info(user, len, 0);
+               break;
 
-               if (*len < sizeof(get)) {
-                       duprintf("get_entries: %u < %u\n", *len, sizeof(get));
-                       ret = -EINVAL;
-               } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
-                       ret = -EFAULT;
-               } else if (*len != sizeof(struct ipt_get_entries) + get.size) {
-                       duprintf("get_entries: %u != %u\n", *len,
-                                sizeof(struct ipt_get_entries) + get.size);
-                       ret = -EINVAL;
-               } else
-                       ret = get_entries(&get, user);
+       case IPT_SO_GET_ENTRIES:
+               ret = get_entries(user, len);
                break;
-       }
 
        case IPT_SO_GET_REVISION_MATCH:
        case IPT_SO_GET_REVISION_TARGET: {
@@ -1336,6 +2203,9 @@ static struct ipt_target ipt_standard_target = {
        .name           = IPT_STANDARD_TARGET,
        .targetsize     = sizeof(int),
        .family         = AF_INET,
+#ifdef CONFIG_COMPAT
+       .compat         = &compat_ipt_standard_fn,
+#endif
 };
 
 static struct ipt_target ipt_error_target = {
@@ -1350,9 +2220,15 @@ static struct nf_sockopt_ops ipt_sockopts = {
        .set_optmin     = IPT_BASE_CTL,
        .set_optmax     = IPT_SO_SET_MAX+1,
        .set            = do_ipt_set_ctl,
+#ifdef CONFIG_COMPAT
+       .compat_set     = compat_do_ipt_set_ctl,
+#endif
        .get_optmin     = IPT_BASE_CTL,
        .get_optmax     = IPT_SO_GET_MAX+1,
        .get            = do_ipt_get_ctl,
+#ifdef CONFIG_COMPAT
+       .compat_get     = compat_do_ipt_get_ctl,
+#endif
 };
 
 static struct ipt_match icmp_matchstruct = {
diff --git a/net/ipv4/netfilter/ipt_multiport.c b/net/ipv4/netfilter/ipt_multiport.c
deleted file mode 100644 (file)
index ac95d83..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-/* Kernel module to match one of a list of TCP/UDP ports: ports are in
-   the same place so we can treat them as equal. */
-
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
- *
- * 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/module.h>
-#include <linux/types.h>
-#include <linux/udp.h>
-#include <linux/skbuff.h>
-
-#include <linux/netfilter_ipv4/ipt_multiport.h>
-#include <linux/netfilter_ipv4/ip_tables.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
-MODULE_DESCRIPTION("iptables multiple port match module");
-
-#if 0
-#define duprintf(format, args...) printk(format , ## args)
-#else
-#define duprintf(format, args...)
-#endif
-
-/* Returns 1 if the port is matched by the test, 0 otherwise. */
-static inline int
-ports_match(const u_int16_t *portlist, enum ipt_multiport_flags flags,
-           u_int8_t count, u_int16_t src, u_int16_t dst)
-{
-       unsigned int i;
-       for (i=0; i<count; i++) {
-               if (flags != IPT_MULTIPORT_DESTINATION
-                   && portlist[i] == src)
-                       return 1;
-
-               if (flags != IPT_MULTIPORT_SOURCE
-                   && portlist[i] == dst)
-                       return 1;
-       }
-
-       return 0;
-}
-
-/* Returns 1 if the port is matched by the test, 0 otherwise. */
-static inline int
-ports_match_v1(const struct ipt_multiport_v1 *minfo,
-              u_int16_t src, u_int16_t dst)
-{
-       unsigned int i;
-       u_int16_t s, e;
-
-       for (i=0; i < minfo->count; i++) {
-               s = minfo->ports[i];
-
-               if (minfo->pflags[i]) {
-                       /* range port matching */
-                       e = minfo->ports[++i];
-                       duprintf("src or dst matches with %d-%d?\n", s, e);
-
-                       if (minfo->flags == IPT_MULTIPORT_SOURCE
-                           && src >= s && src <= e)
-                               return 1 ^ minfo->invert;
-                       if (minfo->flags == IPT_MULTIPORT_DESTINATION
-                           && dst >= s && dst <= e)
-                               return 1 ^ minfo->invert;
-                       if (minfo->flags == IPT_MULTIPORT_EITHER
-                           && ((dst >= s && dst <= e)
-                               || (src >= s && src <= e)))
-                               return 1 ^ minfo->invert;
-               } else {
-                       /* exact port matching */
-                       duprintf("src or dst matches with %d?\n", s);
-
-                       if (minfo->flags == IPT_MULTIPORT_SOURCE
-                           && src == s)
-                               return 1 ^ minfo->invert;
-                       if (minfo->flags == IPT_MULTIPORT_DESTINATION
-                           && dst == s)
-                               return 1 ^ minfo->invert;
-                       if (minfo->flags == IPT_MULTIPORT_EITHER
-                           && (src == s || dst == s))
-                               return 1 ^ minfo->invert;
-               }
-       }
-       return minfo->invert;
-}
-
-static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop)
-{
-       u16 _ports[2], *pptr;
-       const struct ipt_multiport *multiinfo = matchinfo;
-
-       if (offset)
-               return 0;
-
-       pptr = skb_header_pointer(skb, protoff,
-                                 sizeof(_ports), _ports);
-       if (pptr == NULL) {
-               /* We've been asked to examine this packet, and we
-                * can't.  Hence, no choice but to drop.
-                */
-               duprintf("ipt_multiport:"
-                        " Dropping evil offset=0 tinygram.\n");
-               *hotdrop = 1;
-               return 0;
-       }
-
-       return ports_match(multiinfo->ports,
-                          multiinfo->flags, multiinfo->count,
-                          ntohs(pptr[0]), ntohs(pptr[1]));
-}
-
-static int
-match_v1(const struct sk_buff *skb,
-        const struct net_device *in,
-        const struct net_device *out,
-        const struct xt_match *match,
-        const void *matchinfo,
-        int offset,
-        unsigned int protoff,
-        int *hotdrop)
-{
-       u16 _ports[2], *pptr;
-       const struct ipt_multiport_v1 *multiinfo = matchinfo;
-
-       if (offset)
-               return 0;
-
-       pptr = skb_header_pointer(skb, protoff,
-                                 sizeof(_ports), _ports);
-       if (pptr == NULL) {
-               /* We've been asked to examine this packet, and we
-                * can't.  Hence, no choice but to drop.
-                */
-               duprintf("ipt_multiport:"
-                        " Dropping evil offset=0 tinygram.\n");
-               *hotdrop = 1;
-               return 0;
-       }
-
-       return ports_match_v1(multiinfo, ntohs(pptr[0]), ntohs(pptr[1]));
-}
-
-static struct ipt_match multiport_match = {
-       .name           = "multiport",
-       .revision       = 0,
-       .match          = match,
-       .matchsize      = sizeof(struct ipt_multiport),
-       .me             = THIS_MODULE,
-};
-
-static struct ipt_match multiport_match_v1 = {
-       .name           = "multiport",
-       .revision       = 1,
-       .match          = match_v1,
-       .matchsize      = sizeof(struct ipt_multiport_v1),
-       .me             = THIS_MODULE,
-};
-
-static int __init ipt_multiport_init(void)
-{
-       int err;
-
-       err = ipt_register_match(&multiport_match);
-       if (!err) {
-               err = ipt_register_match(&multiport_match_v1);
-               if (err)
-                       ipt_unregister_match(&multiport_match);
-       }
-
-       return err;
-}
-
-static void __exit ipt_multiport_fini(void)
-{
-       ipt_unregister_match(&multiport_match);
-       ipt_unregister_match(&multiport_match_v1);
-}
-
-module_init(ipt_multiport_init);
-module_exit(ipt_multiport_fini);
index 850d919..e1b8f4b 100644 (file)
@@ -68,7 +68,7 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
 {
        int err;
        u32 spi, seq;
-       struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH];
+       struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
        struct xfrm_state *x;
        int xfrm_nr = 0;
        int decaps = 0;
@@ -90,14 +90,16 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
                if (unlikely(x->km.state != XFRM_STATE_VALID))
                        goto drop_unlock;
 
+               if (x->encap->encap_type != encap_type)
+                       goto drop_unlock;
+
                if (x->props.replay_window && xfrm_replay_check(x, seq))
                        goto drop_unlock;
 
                if (xfrm_state_check_expire(x))
                        goto drop_unlock;
 
-               xfrm_vec[xfrm_nr].decap.decap_type = encap_type;
-               if (x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb))
+               if (x->type->input(x, skb))
                        goto drop_unlock;
 
                /* only the first xfrm gets the encap type */
@@ -111,7 +113,7 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
 
                spin_unlock(&x->lock);
 
-               xfrm_vec[xfrm_nr++].xvec = x;
+               xfrm_vec[xfrm_nr++] = x;
 
                iph = skb->nh.iph;
 
@@ -153,7 +155,8 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
        if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
                goto drop;
 
-       memcpy(skb->sp->x+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(struct sec_decap_state));
+       memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec,
+              xfrm_nr * sizeof(xfrm_vec[0]));
        skb->sp->len += xfrm_nr;
 
        nf_reset(skb);
@@ -184,7 +187,7 @@ drop_unlock:
        xfrm_state_put(x);
 drop:
        while (--xfrm_nr >= 0)
-               xfrm_state_put(xfrm_vec[xfrm_nr].xvec);
+               xfrm_state_put(xfrm_vec[xfrm_nr]);
 
        kfree_skb(skb);
        return 0;
index 2d67093..f8ceaa1 100644 (file)
@@ -21,7 +21,7 @@ static int ipip_output(struct xfrm_state *x, struct sk_buff *skb)
        return 0;
 }
 
-static int ipip_xfrm_rcv(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
+static int ipip_xfrm_rcv(struct xfrm_state *x, struct sk_buff *skb)
 {
        return 0;
 }
index cf58251..6778173 100644 (file)
@@ -229,7 +229,7 @@ error:
        return err;
 }
 
-static int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
+static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        /*
         * Before process AH
index 3dcaac7..22f0460 100644 (file)
@@ -130,7 +130,7 @@ error:
        return err;
 }
 
-static int esp6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
+static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        struct ipv6hdr *iph;
        struct ipv6_esp_hdr *esph;
index d4cfec3..00f3fad 100644 (file)
@@ -63,7 +63,7 @@ static void **ipcomp6_scratches;
 static int ipcomp6_scratch_users;
 static LIST_HEAD(ipcomp6_tfms_list);
 
-static int ipcomp6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
+static int ipcomp6_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        int err = 0;
        u8 nexthdr = 0;
index 98f7875..4bc4e5b 100644 (file)
@@ -87,16 +87,6 @@ config IP6_NF_MATCH_HL
 
          To compile it as a module, choose M here.  If unsure, say N.
 
-config IP6_NF_MATCH_MULTIPORT
-       tristate "Multiple port match support"
-       depends on IP6_NF_IPTABLES
-       help
-         Multiport matching allows you to match TCP or UDP packets based on
-         a series of source or destination ports: normally a rule can only
-         match a single range of ports.
-
-         To compile it as a module, choose M here.  If unsure, say N.
-
 config IP6_NF_MATCH_OWNER
        tristate "Owner match support"
        depends on IP6_NF_IPTABLES
@@ -115,11 +105,11 @@ config IP6_NF_MATCH_IPV6HEADER
 
          To compile it as a module, choose M here.  If unsure, say N.
 
-config IP6_NF_MATCH_AHESP
-       tristate "AH/ESP match support"
+config IP6_NF_MATCH_AH
+       tristate "AH match support"
        depends on IP6_NF_IPTABLES
        help
-         This module allows one to match AH and ESP packets.
+         This module allows one to match AH packets.
 
          To compile it as a module, choose M here.  If unsure, say N.
 
index 8436a1a..eeeb57d 100644 (file)
@@ -8,9 +8,8 @@ obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o
 obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o ip6t_dst.o
 obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o
 obj-$(CONFIG_IP6_NF_MATCH_FRAG) += ip6t_frag.o
-obj-$(CONFIG_IP6_NF_MATCH_AHESP) += ip6t_esp.o ip6t_ah.o
+obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
 obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o
-obj-$(CONFIG_IP6_NF_MATCH_MULTIPORT) += ip6t_multiport.o
 obj-$(CONFIG_IP6_NF_MATCH_OWNER) += ip6t_owner.o
 obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o
 obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o
diff --git a/net/ipv6/netfilter/ip6t_esp.c b/net/ipv6/netfilter/ip6t_esp.c
deleted file mode 100644 (file)
index 36bedad..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/* Kernel module to match ESP parameters. */
-/* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu>
- *
- * 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/module.h>
-#include <linux/skbuff.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <linux/types.h>
-#include <net/checksum.h>
-#include <net/ipv6.h>
-
-#include <linux/netfilter_ipv6/ip6_tables.h>
-#include <linux/netfilter_ipv6/ip6t_esp.h>
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("IPv6 ESP match");
-MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
-/* Returns 1 if the spi is matched by the range, 0 otherwise */
-static inline int
-spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
-{
-       int r=0;
-       DEBUGP("esp spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
-              min,spi,max);
-       r=(spi >= min && spi <= max) ^ invert;
-       DEBUGP(" result %s\n",r? "PASS\n" : "FAILED\n");
-       return r;
-}
-
-static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop)
-{
-       struct ip_esp_hdr _esp, *eh;
-       const struct ip6t_esp *espinfo = matchinfo;
-       unsigned int ptr;
-
-       /* Make sure this isn't an evil packet */
-       /*DEBUGP("ipv6_esp entered \n");*/
-
-       if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ESP, NULL) < 0)
-               return 0;
-
-       eh = skb_header_pointer(skb, ptr, sizeof(_esp), &_esp);
-       if (eh == NULL) {
-               *hotdrop = 1;
-               return 0;
-       }
-
-       DEBUGP("IPv6 ESP SPI %u %08X\n", ntohl(eh->spi), ntohl(eh->spi));
-
-       return (eh != NULL)
-               && spi_match(espinfo->spis[0], espinfo->spis[1],
-                             ntohl(eh->spi),
-                             !!(espinfo->invflags & IP6T_ESP_INV_SPI));
-}
-
-/* Called when user tries to insert an entry of this type. */
-static int
-checkentry(const char *tablename,
-          const void *ip,
-          const struct xt_match *match,
-          void *matchinfo,
-          unsigned int matchinfosize,
-          unsigned int hook_mask)
-{
-       const struct ip6t_esp *espinfo = matchinfo;
-
-       if (espinfo->invflags & ~IP6T_ESP_INV_MASK) {
-               DEBUGP("ip6t_esp: unknown flags %X\n",
-                        espinfo->invflags);
-               return 0;
-       }
-       return 1;
-}
-
-static struct ip6t_match esp_match = {
-       .name           = "esp",
-       .match          = match,
-       .matchsize      = sizeof(struct ip6t_esp),
-       .checkentry     = checkentry,
-       .me             = THIS_MODULE,
-};
-
-static int __init ip6t_esp_init(void)
-{
-       return ip6t_register_match(&esp_match);
-}
-
-static void __exit ip6t_esp_fini(void)
-{
-       ip6t_unregister_match(&esp_match);
-}
-
-module_init(ip6t_esp_init);
-module_exit(ip6t_esp_fini);
diff --git a/net/ipv6/netfilter/ip6t_multiport.c b/net/ipv6/netfilter/ip6t_multiport.c
deleted file mode 100644 (file)
index 10c48ba..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/* Kernel module to match one of a list of TCP/UDP ports: ports are in
-   the same place so we can treat them as equal. */
-
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
- *
- * 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/module.h>
-#include <linux/types.h>
-#include <linux/udp.h>
-#include <linux/skbuff.h>
-#include <linux/in.h>
-
-#include <linux/netfilter_ipv6/ip6t_multiport.h>
-#include <linux/netfilter_ipv6/ip6_tables.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
-MODULE_DESCRIPTION("ip6tables match for multiple ports");
-
-#if 0
-#define duprintf(format, args...) printk(format , ## args)
-#else
-#define duprintf(format, args...)
-#endif
-
-/* Returns 1 if the port is matched by the test, 0 otherwise. */
-static inline int
-ports_match(const u_int16_t *portlist, enum ip6t_multiport_flags flags,
-           u_int8_t count, u_int16_t src, u_int16_t dst)
-{
-       unsigned int i;
-       for (i=0; i<count; i++) {
-               if (flags != IP6T_MULTIPORT_DESTINATION
-                   && portlist[i] == src)
-                       return 1;
-
-               if (flags != IP6T_MULTIPORT_SOURCE
-                   && portlist[i] == dst)
-                       return 1;
-       }
-
-       return 0;
-}
-
-static int
-match(const struct sk_buff *skb,
-      const struct net_device *in,
-      const struct net_device *out,
-      const struct xt_match *match,
-      const void *matchinfo,
-      int offset,
-      unsigned int protoff,
-      int *hotdrop)
-{
-       u16 _ports[2], *pptr;
-       const struct ip6t_multiport *multiinfo = matchinfo;
-
-       /* Must not be a fragment. */
-       if (offset)
-               return 0;
-
-       /* Must be big enough to read ports (both UDP and TCP have
-          them at the start). */
-       pptr = skb_header_pointer(skb, protoff, sizeof(_ports), &_ports[0]);
-       if (pptr == NULL) {
-               /* We've been asked to examine this packet, and we
-                * can't.  Hence, no choice but to drop.
-                */
-               duprintf("ip6t_multiport:"
-                        " Dropping evil offset=0 tinygram.\n");
-               *hotdrop = 1;
-               return 0;
-       }
-
-       return ports_match(multiinfo->ports,
-                          multiinfo->flags, multiinfo->count,
-                          ntohs(pptr[0]), ntohs(pptr[1]));
-}
-
-/* Called when user tries to insert an entry of this type. */
-static int
-checkentry(const char *tablename,
-          const void *info,
-          const struct xt_match *match,
-          void *matchinfo,
-          unsigned int matchsize,
-          unsigned int hook_mask)
-{
-       const struct ip6t_ip6 *ip = info;
-       const struct ip6t_multiport *multiinfo = matchinfo;
-
-       /* Must specify proto == TCP/UDP, no unknown flags or bad count */
-       return (ip->proto == IPPROTO_TCP || ip->proto == IPPROTO_UDP)
-               && !(ip->invflags & IP6T_INV_PROTO)
-               && (multiinfo->flags == IP6T_MULTIPORT_SOURCE
-                   || multiinfo->flags == IP6T_MULTIPORT_DESTINATION
-                   || multiinfo->flags == IP6T_MULTIPORT_EITHER)
-               && multiinfo->count <= IP6T_MULTI_PORTS;
-}
-
-static struct ip6t_match multiport_match = {
-       .name           = "multiport",
-       .match          = match,
-       .matchsize      = sizeof(struct ip6t_multiport),
-       .checkentry     = checkentry,
-       .me             = THIS_MODULE,
-};
-
-static int __init ip6t_multiport_init(void)
-{
-       return ip6t_register_match(&multiport_match);
-}
-
-static void __exit ip6t_multiport_fini(void)
-{
-       ip6t_unregister_match(&multiport_match);
-}
-
-module_init(ip6t_multiport_init);
-module_exit(ip6t_multiport_fini);
index cccf8b7..00cfdee 100644 (file)
@@ -32,7 +32,7 @@ int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi)
 {
        int err;
        u32 seq;
-       struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH];
+       struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
        struct xfrm_state *x;
        int xfrm_nr = 0;
        int decaps = 0;
@@ -65,7 +65,7 @@ int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi)
                if (xfrm_state_check_expire(x))
                        goto drop_unlock;
 
-               nexthdr = x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb);
+               nexthdr = x->type->input(x, skb);
                if (nexthdr <= 0)
                        goto drop_unlock;
 
@@ -79,7 +79,7 @@ int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi)
 
                spin_unlock(&x->lock);
 
-               xfrm_vec[xfrm_nr++].xvec = x;
+               xfrm_vec[xfrm_nr++] = x;
 
                if (x->props.mode) { /* XXX */
                        if (nexthdr != IPPROTO_IPV6)
@@ -118,7 +118,8 @@ int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi)
        if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
                goto drop;
 
-       memcpy(skb->sp->x+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(struct sec_decap_state));
+       memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec,
+              xfrm_nr * sizeof(xfrm_vec[0]));
        skb->sp->len += xfrm_nr;
        skb->ip_summed = CHECKSUM_NONE;
 
@@ -149,7 +150,7 @@ drop_unlock:
        xfrm_state_put(x);
 drop:
        while (--xfrm_nr >= 0)
-               xfrm_state_put(xfrm_vec[xfrm_nr].xvec);
+               xfrm_state_put(xfrm_vec[xfrm_nr]);
        kfree_skb(skb);
        return -1;
 }
index a8f6776..d37768e 100644 (file)
@@ -351,7 +351,7 @@ static int xfrm6_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
        return 0;
 }
 
-static int xfrm6_tunnel_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
+static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        return 0;
 }
index 332acb3..e2893ef 100644 (file)
@@ -231,6 +231,15 @@ config NETFILTER_XT_MATCH_DCCP
          If you want to compile it as a module, say M here and read
          <file:Documentation/modules.txt>.  If unsure, say `N'.
 
+config NETFILTER_XT_MATCH_ESP
+       tristate '"ESP" match support'
+       depends on NETFILTER_XTABLES
+       help
+         This match extension allows you to match a range of SPIs
+         inside ESP header of IPSec packets.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
 config NETFILTER_XT_MATCH_HELPER
        tristate '"helper" match support'
        depends on NETFILTER_XTABLES
@@ -289,6 +298,16 @@ config NETFILTER_XT_MATCH_POLICY
 
          To compile it as a module, choose M here.  If unsure, say N.
 
+config NETFILTER_XT_MATCH_MULTIPORT
+       tristate "Multiple port match support"
+       depends on NETFILTER_XTABLES
+       help
+         Multiport matching allows you to match TCP or UDP packets based on
+         a series of source or destination ports: normally a rule can only
+         match a single range of ports.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
 config NETFILTER_XT_MATCH_PHYSDEV
        tristate '"physdev" match support'
        depends on NETFILTER_XTABLES && BRIDGE_NETFILTER
index 9558727..95b7e41 100644 (file)
@@ -35,11 +35,13 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNMARK) += xt_connmark.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_DCCP) += xt_dccp.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_ESP) += xt_esp.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_HELPER) += xt_helper.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_length.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o
index 0e0e9d7..bd10eb9 100644 (file)
@@ -1022,7 +1022,7 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nfattr *cda[])
                        return err;
        }
 
-#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
+#if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK-1])
                ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
 #endif
@@ -1062,7 +1062,7 @@ ctnetlink_create_conntrack(struct nfattr *cda[],
                        return err;
        }
 
-#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
+#if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK-1])
                ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
 #endif
@@ -1687,7 +1687,7 @@ static void __exit ctnetlink_exit(void)
        printk("ctnetlink: unregistering from nfnetlink.\n");
 
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
-       nf_conntrack_unregister_notifier(&ctnl_notifier_exp);
+       nf_conntrack_expect_unregister_notifier(&ctnl_notifier_exp);
        nf_conntrack_unregister_notifier(&ctnl_notifier);
 #endif
 
index a657ab5..feb8a9e 100644 (file)
@@ -38,6 +38,7 @@ struct xt_af {
        struct list_head match;
        struct list_head target;
        struct list_head tables;
+       struct mutex compat_mutex;
 };
 
 static struct xt_af *xt;
@@ -272,6 +273,54 @@ int xt_check_match(const struct xt_match *match, unsigned short family,
 }
 EXPORT_SYMBOL_GPL(xt_check_match);
 
+#ifdef CONFIG_COMPAT
+int xt_compat_match(void *match, void **dstptr, int *size, int convert)
+{
+       struct xt_match *m;
+       struct compat_xt_entry_match *pcompat_m;
+       struct xt_entry_match *pm;
+       u_int16_t msize;
+       int off, ret;
+
+       ret = 0;
+       m = ((struct xt_entry_match *)match)->u.kernel.match;
+       off = XT_ALIGN(m->matchsize) - COMPAT_XT_ALIGN(m->matchsize);
+       switch (convert) {
+               case COMPAT_TO_USER:
+                       pm = (struct xt_entry_match *)match;
+                       msize = pm->u.user.match_size;
+                       if (__copy_to_user(*dstptr, pm, msize)) {
+                               ret = -EFAULT;
+                               break;
+                       }
+                       msize -= off;
+                       if (put_user(msize, (u_int16_t *)*dstptr))
+                               ret = -EFAULT;
+                       *size -= off;
+                       *dstptr += msize;
+                       break;
+               case COMPAT_FROM_USER:
+                       pcompat_m = (struct compat_xt_entry_match *)match;
+                       pm = (struct xt_entry_match *)*dstptr;
+                       msize = pcompat_m->u.user.match_size;
+                       memcpy(pm, pcompat_m, msize);
+                       msize += off;
+                       pm->u.user.match_size = msize;
+                       *size += off;
+                       *dstptr += msize;
+                       break;
+               case COMPAT_CALC_SIZE:
+                       *size += off;
+                       break;
+               default:
+                       ret = -ENOPROTOOPT;
+                       break;
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(xt_compat_match);
+#endif
+
 int xt_check_target(const struct xt_target *target, unsigned short family,
                    unsigned int size, const char *table, unsigned int hook_mask,
                    unsigned short proto, int inv_proto)
@@ -301,6 +350,54 @@ int xt_check_target(const struct xt_target *target, unsigned short family,
 }
 EXPORT_SYMBOL_GPL(xt_check_target);
 
+#ifdef CONFIG_COMPAT
+int xt_compat_target(void *target, void **dstptr, int *size, int convert)
+{
+       struct xt_target *t;
+       struct compat_xt_entry_target *pcompat;
+       struct xt_entry_target *pt;
+       u_int16_t tsize;
+       int off, ret;
+
+       ret = 0;
+       t = ((struct xt_entry_target *)target)->u.kernel.target;
+       off = XT_ALIGN(t->targetsize) - COMPAT_XT_ALIGN(t->targetsize);
+       switch (convert) {
+               case COMPAT_TO_USER:
+                       pt = (struct xt_entry_target *)target;
+                       tsize = pt->u.user.target_size;
+                       if (__copy_to_user(*dstptr, pt, tsize)) {
+                               ret = -EFAULT;
+                               break;
+                       }
+                       tsize -= off;
+                       if (put_user(tsize, (u_int16_t *)*dstptr))
+                               ret = -EFAULT;
+                       *size -= off;
+                       *dstptr += tsize;
+                       break;
+               case COMPAT_FROM_USER:
+                       pcompat = (struct compat_xt_entry_target *)target;
+                       pt = (struct xt_entry_target *)*dstptr;
+                       tsize = pcompat->u.user.target_size;
+                       memcpy(pt, pcompat, tsize);
+                       tsize += off;
+                       pt->u.user.target_size = tsize;
+                       *size += off;
+                       *dstptr += tsize;
+                       break;
+               case COMPAT_CALC_SIZE:
+                       *size += off;
+                       break;
+               default:
+                       ret = -ENOPROTOOPT;
+                       break;
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(xt_compat_target);
+#endif
+
 struct xt_table_info *xt_alloc_table_info(unsigned int size)
 {
        struct xt_table_info *newinfo;
@@ -371,6 +468,19 @@ void xt_table_unlock(struct xt_table *table)
 }
 EXPORT_SYMBOL_GPL(xt_table_unlock);
 
+#ifdef CONFIG_COMPAT
+void xt_compat_lock(int af)
+{
+       mutex_lock(&xt[af].compat_mutex);
+}
+EXPORT_SYMBOL_GPL(xt_compat_lock);
+
+void xt_compat_unlock(int af)
+{
+       mutex_unlock(&xt[af].compat_mutex);
+}
+EXPORT_SYMBOL_GPL(xt_compat_unlock);
+#endif
 
 struct xt_table_info *
 xt_replace_table(struct xt_table *table,
@@ -671,6 +781,9 @@ static int __init xt_init(void)
 
        for (i = 0; i < NPROTO; i++) {
                mutex_init(&xt[i].mutex);
+#ifdef CONFIG_COMPAT
+               mutex_init(&xt[i].compat_mutex);
+#endif
                INIT_LIST_HEAD(&xt[i].target);
                INIT_LIST_HEAD(&xt[i].match);
                INIT_LIST_HEAD(&xt[i].tables);
similarity index 51%
rename from net/ipv4/netfilter/ipt_esp.c
rename to net/netfilter/xt_esp.c
index 3840b41..9dad628 100644 (file)
@@ -9,16 +9,22 @@
 
 #include <linux/module.h>
 #include <linux/skbuff.h>
+#include <linux/in.h>
 #include <linux/ip.h>
 
-#include <linux/netfilter_ipv4/ipt_esp.h>
+#include <linux/netfilter/xt_esp.h>
+#include <linux/netfilter/x_tables.h>
+
 #include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Yon Uriarte <yon@astaro.de>");
-MODULE_DESCRIPTION("iptables ESP SPI match module");
+MODULE_DESCRIPTION("x_tables ESP SPI match module");
+MODULE_ALIAS("ipt_esp");
+MODULE_ALIAS("ip6t_esp");
 
-#ifdef DEBUG_CONNTRACK
+#if 0
 #define duprintf(format, args...) printk(format , ## args)
 #else
 #define duprintf(format, args...)
@@ -28,11 +34,11 @@ MODULE_DESCRIPTION("iptables ESP SPI match module");
 static inline int
 spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
 {
-       int r=0;
-        duprintf("esp spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
-               min,spi,max);
-       r=(spi >= min && spi <= max) ^ invert;
-       duprintf(" result %s\n",r? "PASS" : "FAILED");
+       int r = 0;
+       duprintf("esp spi_match:%c 0x%x <= 0x%x <= 0x%x", invert ? '!' : ' ',
+                min, spi, max);
+       r = (spi >= min && spi <= max) ^ invert;
+       duprintf(" result %s\n", r ? "PASS" : "FAILED");
        return r;
 }
 
@@ -47,14 +53,13 @@ match(const struct sk_buff *skb,
       int *hotdrop)
 {
        struct ip_esp_hdr _esp, *eh;
-       const struct ipt_esp *espinfo = matchinfo;
+       const struct xt_esp *espinfo = matchinfo;
 
        /* Must not be a fragment. */
        if (offset)
                return 0;
 
-       eh = skb_header_pointer(skb, protoff,
-                               sizeof(_esp), &_esp);
+       eh = skb_header_pointer(skb, protoff, sizeof(_esp), &_esp);
        if (eh == NULL) {
                /* We've been asked to examine this packet, and we
                 * can't.  Hence, no choice but to drop.
@@ -64,9 +69,8 @@ match(const struct sk_buff *skb,
                return 0;
        }
 
-       return spi_match(espinfo->spis[0], espinfo->spis[1],
-                        ntohl(eh->spi),
-                        !!(espinfo->invflags & IPT_ESP_INV_SPI));
+       return spi_match(espinfo->spis[0], espinfo->spis[1], ntohl(eh->spi),
+                        !!(espinfo->invflags & XT_ESP_INV_SPI));
 }
 
 /* Called when user tries to insert an entry of this type. */
@@ -78,34 +82,55 @@ checkentry(const char *tablename,
           unsigned int matchinfosize,
           unsigned int hook_mask)
 {
-       const struct ipt_esp *espinfo = matchinfo;
+       const struct xt_esp *espinfo = matchinfo;
 
-       /* Must specify no unknown invflags */
-       if (espinfo->invflags & ~IPT_ESP_INV_MASK) {
-               duprintf("ipt_esp: unknown flags %X\n", espinfo->invflags);
+       if (espinfo->invflags & ~XT_ESP_INV_MASK) {
+               duprintf("xt_esp: unknown flags %X\n", espinfo->invflags);
                return 0;
        }
+
        return 1;
 }
 
-static struct ipt_match esp_match = {
+static struct xt_match esp_match = {
        .name           = "esp",
-       .match          = match,
-       .matchsize      = sizeof(struct ipt_esp),
+       .family         = AF_INET,
        .proto          = IPPROTO_ESP,
-       .checkentry     = checkentry,
+       .match          = &match,
+       .matchsize      = sizeof(struct xt_esp),
+       .checkentry     = &checkentry,
        .me             = THIS_MODULE,
 };
 
-static int __init ipt_esp_init(void)
+static struct xt_match esp6_match = {
+       .name           = "esp",
+       .family         = AF_INET6,
+       .proto          = IPPROTO_ESP,
+       .match          = &match,
+       .matchsize      = sizeof(struct xt_esp),
+       .checkentry     = &checkentry,
+       .me             = THIS_MODULE,
+};
+
+static int __init xt_esp_init(void)
 {
-       return ipt_register_match(&esp_match);
+       int ret;
+       ret = xt_register_match(&esp_match);
+       if (ret)
+               return ret;
+
+       ret = xt_register_match(&esp6_match);
+       if (ret)
+               xt_unregister_match(&esp_match);
+
+       return ret;
 }
 
-static void __exit ipt_esp_fini(void)
+static void __exit xt_esp_cleanup(void)
 {
-       ipt_unregister_match(&esp_match);
+       xt_unregister_match(&esp_match);
+       xt_unregister_match(&esp6_match);
 }
 
-module_init(ipt_esp_init);
-module_exit(ipt_esp_fini);
+module_init(xt_esp_init);
+module_exit(xt_esp_cleanup);
diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c
new file mode 100644 (file)
index 0000000..b56cd2b
--- /dev/null
@@ -0,0 +1,314 @@
+/* Kernel module to match one of a list of TCP/UDP ports: ports are in
+   the same place so we can treat them as equal. */
+
+/* (C) 1999-2001 Paul `Rusty' Russell
+ * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
+ *
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/udp.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+
+#include <linux/netfilter/xt_multiport.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
+MODULE_DESCRIPTION("x_tables multiple port match module");
+MODULE_ALIAS("ipt_multiport");
+MODULE_ALIAS("ip6t_multiport");
+
+#if 0
+#define duprintf(format, args...) printk(format , ## args)
+#else
+#define duprintf(format, args...)
+#endif
+
+/* Returns 1 if the port is matched by the test, 0 otherwise. */
+static inline int
+ports_match(const u_int16_t *portlist, enum xt_multiport_flags flags,
+           u_int8_t count, u_int16_t src, u_int16_t dst)
+{
+       unsigned int i;
+       for (i = 0; i < count; i++) {
+               if (flags != XT_MULTIPORT_DESTINATION && portlist[i] == src)
+                       return 1;
+
+               if (flags != XT_MULTIPORT_SOURCE && portlist[i] == dst)
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* Returns 1 if the port is matched by the test, 0 otherwise. */
+static inline int
+ports_match_v1(const struct xt_multiport_v1 *minfo,
+              u_int16_t src, u_int16_t dst)
+{
+       unsigned int i;
+       u_int16_t s, e;
+
+       for (i = 0; i < minfo->count; i++) {
+               s = minfo->ports[i];
+
+               if (minfo->pflags[i]) {
+                       /* range port matching */
+                       e = minfo->ports[++i];
+                       duprintf("src or dst matches with %d-%d?\n", s, e);
+
+                       if (minfo->flags == XT_MULTIPORT_SOURCE
+                           && src >= s && src <= e)
+                               return 1 ^ minfo->invert;
+                       if (minfo->flags == XT_MULTIPORT_DESTINATION
+                           && dst >= s && dst <= e)
+                               return 1 ^ minfo->invert;
+                       if (minfo->flags == XT_MULTIPORT_EITHER
+                           && ((dst >= s && dst <= e)
+                               || (src >= s && src <= e)))
+                               return 1 ^ minfo->invert;
+               } else {
+                       /* exact port matching */
+                       duprintf("src or dst matches with %d?\n", s);
+
+                       if (minfo->flags == XT_MULTIPORT_SOURCE
+                           && src == s)
+                               return 1 ^ minfo->invert;
+                       if (minfo->flags == XT_MULTIPORT_DESTINATION
+                           && dst == s)
+                               return 1 ^ minfo->invert;
+                       if (minfo->flags == XT_MULTIPORT_EITHER
+                           && (src == s || dst == s))
+                               return 1 ^ minfo->invert;
+               }
+       }
+
+       return minfo->invert;
+}
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const struct xt_match *match,
+      const void *matchinfo,
+      int offset,
+      unsigned int protoff,
+      int *hotdrop)
+{
+       u16 _ports[2], *pptr;
+       const struct xt_multiport *multiinfo = matchinfo;
+
+       if (offset)
+               return 0;
+
+       pptr = skb_header_pointer(skb, protoff, sizeof(_ports), _ports);
+       if (pptr == NULL) {
+               /* We've been asked to examine this packet, and we
+                * can't.  Hence, no choice but to drop.
+                */
+               duprintf("xt_multiport: Dropping evil offset=0 tinygram.\n");
+               *hotdrop = 1;
+               return 0;
+       }
+
+       return ports_match(multiinfo->ports,
+                          multiinfo->flags, multiinfo->count,
+                          ntohs(pptr[0]), ntohs(pptr[1]));
+}
+
+static int
+match_v1(const struct sk_buff *skb,
+        const struct net_device *in,
+        const struct net_device *out,
+        const struct xt_match *match,
+        const void *matchinfo,
+        int offset,
+        unsigned int protoff,
+        int *hotdrop)
+{
+       u16 _ports[2], *pptr;
+       const struct xt_multiport_v1 *multiinfo = matchinfo;
+
+       if (offset)
+               return 0;
+
+       pptr = skb_header_pointer(skb, protoff, sizeof(_ports), _ports);
+       if (pptr == NULL) {
+               /* We've been asked to examine this packet, and we
+                * can't.  Hence, no choice but to drop.
+                */
+               duprintf("xt_multiport: Dropping evil offset=0 tinygram.\n");
+               *hotdrop = 1;
+               return 0;
+       }
+
+       return ports_match_v1(multiinfo, ntohs(pptr[0]), ntohs(pptr[1]));
+}
+
+static inline int
+check(u_int16_t proto,
+      u_int8_t ip_invflags,
+      u_int8_t match_flags,
+      u_int8_t count)
+{
+       /* Must specify proto == TCP/UDP, no unknown flags or bad count */
+       return (proto == IPPROTO_TCP || proto == IPPROTO_UDP)
+               && !(ip_invflags & XT_INV_PROTO)
+               && (match_flags == XT_MULTIPORT_SOURCE
+                   || match_flags == XT_MULTIPORT_DESTINATION
+                   || match_flags == XT_MULTIPORT_EITHER)
+               && count <= XT_MULTI_PORTS;
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const void *info,
+          const struct xt_match *match,
+          void *matchinfo,
+          unsigned int matchsize,
+          unsigned int hook_mask)
+{
+       const struct ipt_ip *ip = info;
+       const struct xt_multiport *multiinfo = matchinfo;
+
+       return check(ip->proto, ip->invflags, multiinfo->flags,
+                    multiinfo->count);
+}
+
+static int
+checkentry_v1(const char *tablename,
+             const void *info,
+             const struct xt_match *match,
+             void *matchinfo,
+             unsigned int matchsize,
+             unsigned int hook_mask)
+{
+       const struct ipt_ip *ip = info;
+       const struct xt_multiport_v1 *multiinfo = matchinfo;
+
+       return check(ip->proto, ip->invflags, multiinfo->flags,
+                    multiinfo->count);
+}
+
+static int
+checkentry6(const char *tablename,
+           const void *info,
+           const struct xt_match *match,
+           void *matchinfo,
+           unsigned int matchsize,
+           unsigned int hook_mask)
+{
+       const struct ip6t_ip6 *ip = info;
+       const struct xt_multiport *multiinfo = matchinfo;
+
+       return check(ip->proto, ip->invflags, multiinfo->flags,
+                    multiinfo->count);
+}
+
+static int
+checkentry6_v1(const char *tablename,
+              const void *info,
+              const struct xt_match *match,
+              void *matchinfo,
+              unsigned int matchsize,
+              unsigned int hook_mask)
+{
+       const struct ip6t_ip6 *ip = info;
+       const struct xt_multiport_v1 *multiinfo = matchinfo;
+
+       return check(ip->proto, ip->invflags, multiinfo->flags,
+                    multiinfo->count);
+}
+
+static struct xt_match multiport_match = {
+       .name           = "multiport",
+       .revision       = 0,
+       .matchsize      = sizeof(struct xt_multiport),
+       .match          = &match,
+       .checkentry     = &checkentry,
+       .family         = AF_INET,
+       .me             = THIS_MODULE,
+};
+
+static struct xt_match multiport_match_v1 = {
+       .name           = "multiport",
+       .revision       = 1,
+       .matchsize      = sizeof(struct xt_multiport_v1),
+       .match          = &match_v1,
+       .checkentry     = &checkentry_v1,
+       .family         = AF_INET,
+       .me             = THIS_MODULE,
+};
+
+static struct xt_match multiport6_match = {
+       .name           = "multiport",
+       .revision       = 0,
+       .matchsize      = sizeof(struct xt_multiport),
+       .match          = &match,
+       .checkentry     = &checkentry6,
+       .family         = AF_INET6,
+       .me             = THIS_MODULE,
+};
+
+static struct xt_match multiport6_match_v1 = {
+       .name           = "multiport",
+       .revision       = 1,
+       .matchsize      = sizeof(struct xt_multiport_v1),
+       .match          = &match_v1,
+       .checkentry     = &checkentry6_v1,
+       .family         = AF_INET6,
+       .me             = THIS_MODULE,
+};
+
+static int __init xt_multiport_init(void)
+{
+       int ret;
+
+       ret = xt_register_match(&multiport_match);
+       if (ret)
+               goto out;
+
+       ret = xt_register_match(&multiport_match_v1);
+       if (ret)
+               goto out_unreg_multi_v0;
+
+       ret = xt_register_match(&multiport6_match);
+       if (ret)
+               goto out_unreg_multi_v1;
+
+       ret = xt_register_match(&multiport6_match_v1);
+       if (ret)
+               goto out_unreg_multi6_v0;
+
+       return ret;
+
+out_unreg_multi6_v0:
+       xt_unregister_match(&multiport6_match);
+out_unreg_multi_v1:
+       xt_unregister_match(&multiport_match_v1);
+out_unreg_multi_v0:
+       xt_unregister_match(&multiport_match);
+out:
+       return ret;
+}
+
+static void __exit xt_multiport_fini(void)
+{
+       xt_unregister_match(&multiport_match);
+       xt_unregister_match(&multiport_match_v1);
+       xt_unregister_match(&multiport6_match);
+       xt_unregister_match(&multiport6_match_v1);
+}
+
+module_init(xt_multiport_init);
+module_exit(xt_multiport_fini);
index 1099cb0..a3aa62f 100644 (file)
@@ -71,7 +71,7 @@ match_policy_in(const struct sk_buff *skb, const struct xt_policy_info *info,
                        return 0;
                e = &info->pol[pos];
 
-               if (match_xfrm_state(sp->x[i].xvec, e, family)) {
+               if (match_xfrm_state(sp->xvec[i], e, family)) {
                        if (!strict)
                                return 1;
                } else if (strict)
index b13042f..b807f36 100644 (file)
@@ -1418,7 +1418,8 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int _
        newfd = sock_alloc_fd(&newfile);
        if (unlikely(newfd < 0)) {
                err = newfd;
-               goto out_release;
+               sock_release(newsock);
+               goto out_put;
        }
 
        err = sock_attach_fd(newsock, newfile);
@@ -1455,10 +1456,8 @@ out_put:
 out:
        return err;
 out_fd:
-       put_filp(newfile);
+       fput(newfile);
        put_unused_fd(newfd);
-out_release:
-       sock_release(newsock);
        goto out_put;
 }
 
index 2407a70..b549710 100644 (file)
@@ -18,7 +18,7 @@ void __secpath_destroy(struct sec_path *sp)
 {
        int i;
        for (i = 0; i < sp->len; i++)
-               xfrm_state_put(sp->x[i].xvec);
+               xfrm_state_put(sp->xvec[i]);
        kmem_cache_free(secpath_cachep, sp);
 }
 EXPORT_SYMBOL(__secpath_destroy);
@@ -37,7 +37,7 @@ struct sec_path *secpath_dup(struct sec_path *src)
 
                memcpy(sp, src, sizeof(*sp));
                for (i = 0; i < sp->len; i++)
-                       xfrm_state_hold(sp->x[i].xvec);
+                       xfrm_state_hold(sp->xvec[i]);
        }
        atomic_set(&sp->refcnt, 1);
        return sp;
index f5eae9f..c3725fe 100644 (file)
@@ -943,9 +943,9 @@ xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
        } else
                start = -1;
        for (; idx < sp->len; idx++) {
-               if (xfrm_state_ok(tmpl, sp->x[idx].xvec, family))
+               if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
                        return ++idx;
-               if (sp->x[idx].xvec->props.mode)
+               if (sp->xvec[idx]->props.mode)
                        break;
        }
        return start;
@@ -968,7 +968,7 @@ EXPORT_SYMBOL(xfrm_decode_session);
 static inline int secpath_has_tunnel(struct sec_path *sp, int k)
 {
        for (; k < sp->len; k++) {
-               if (sp->x[k].xvec->props.mode)
+               if (sp->xvec[k]->props.mode)
                        return 1;
        }
 
@@ -994,8 +994,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
                int i;
 
                for (i=skb->sp->len-1; i>=0; i--) {
-                       struct sec_decap_state *xvec = &(skb->sp->x[i]);
-                       if (!xfrm_selector_match(&xvec->xvec->sel, &fl, family))
+                       struct xfrm_state *x = skb->sp->xvec[i];
+                       if (!xfrm_selector_match(&x->sel, &fl, family))
                                return 0;
                }
        }
index 9dd121b..8efc1b1 100644 (file)
@@ -92,8 +92,9 @@ config SND_SEQUENCER_OSS
 
          Many programs still use the OSS API, so say Y.
 
-         To compile this driver as a module, choose M here: the module
-         will be called snd-seq-oss.
+         If you choose M in "Sequencer support" (SND_SEQUENCER),
+         this will be compiled as a module. The module will be called
+         snd-seq-oss.
 
 config SND_RTCTIMER
        tristate "RTC Timer support"
index 5747453..22565c9 100644 (file)
@@ -664,7 +664,7 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
        if (copy_from_user(&info, _info, sizeof(info)))
                return -EFAULT;
        snd_power_lock(ctl->card);
-       result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0, NULL);
+       result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
        if (result >= 0)
                result = snd_ctl_elem_info(ctl, &info);
        snd_power_unlock(ctl->card);
@@ -718,7 +718,7 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
                return -EFAULT;
        }
        snd_power_lock(card);
-       result = snd_power_wait(card, SNDRV_CTL_POWER_D0, NULL);
+       result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
        if (result >= 0)
                result = snd_ctl_elem_read(card, control);
        snd_power_unlock(card);
@@ -783,7 +783,7 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
        }
        card = file->card;
        snd_power_lock(card);
-       result = snd_power_wait(card, SNDRV_CTL_POWER_D0, NULL);
+       result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
        if (result >= 0)
                result = snd_ctl_elem_write(card, file, control);
        snd_power_unlock(card);
index 84fef50..3c0161b 100644 (file)
@@ -109,7 +109,7 @@ static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
                goto error;
 
        snd_power_lock(ctl->card);
-       err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0, NULL);
+       err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
        if (err >= 0)
                err = snd_ctl_elem_info(ctl, data);
        snd_power_unlock(ctl->card);
@@ -294,7 +294,7 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
                goto error;
 
        snd_power_lock(card);
-       err = snd_power_wait(card, SNDRV_CTL_POWER_D0, NULL);
+       err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
        if (err >= 0)
                err = snd_ctl_elem_read(card, data);
        snd_power_unlock(card);
@@ -320,7 +320,7 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
                goto error;
 
        snd_power_lock(card);
-       err = snd_power_wait(card, SNDRV_CTL_POWER_D0, NULL);
+       err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
        if (err >= 0)
                err = snd_ctl_elem_write(card, file, data);
        snd_power_unlock(card);
index 5bb8a8b..39ed2e5 100644 (file)
@@ -722,13 +722,12 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
  *  snd_power_wait - wait until the power-state is changed.
  *  @card: soundcard structure
  *  @power_state: expected power state
- *  @file: file structure for the O_NONBLOCK check (optional)
  *
  *  Waits until the power-state is changed.
  *
  *  Note: the power lock must be active before call.
  */
-int snd_power_wait(struct snd_card *card, unsigned int power_state, struct file *file)
+int snd_power_wait(struct snd_card *card, unsigned int power_state)
 {
        wait_queue_t wait;
        int result = 0;
@@ -745,12 +744,6 @@ int snd_power_wait(struct snd_card *card, unsigned int power_state, struct file
                }
                if (snd_power_get_state(card) == power_state)
                        break;
-#if 0 /* block all devices */
-               if (file && (file->f_flags & O_NONBLOCK)) {
-                       result = -EAGAIN;
-                       break;
-               }
-#endif
                set_current_state(TASK_UNINTERRUPTIBLE);
                snd_power_unlock(card);
                schedule_timeout(30 * HZ);
index f8302b7..91114c7 100644 (file)
@@ -208,9 +208,8 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
                        oss_buffer_size = runtime->oss.mmap_bytes;
        }
 
-       if (substream->oss.setup &&
-           substream->oss.setup->period_size > 16)
-               oss_period_size = substream->oss.setup->period_size;
+       if (substream->oss.setup.period_size > 16)
+               oss_period_size = substream->oss.setup.period_size;
        else if (runtime->oss.fragshift) {
                oss_period_size = 1 << runtime->oss.fragshift;
                if (oss_period_size > oss_buffer_size / 2)
@@ -252,10 +251,8 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
 
        oss_periods = oss_buffer_size / oss_period_size;
 
-       if (substream->oss.setup) {
-               if (substream->oss.setup->periods > 1)
-                       oss_periods = substream->oss.setup->periods;
-       }
+       if (substream->oss.setup.periods > 1)
+               oss_periods = substream->oss.setup.periods;
 
        s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
        if (runtime->oss.maxfrags && s > runtime->oss.maxfrags)
@@ -341,12 +338,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
                goto failure;
        }
 
-       if (atomic_read(&runtime->mmap_count)) {
+       if (atomic_read(&runtime->mmap_count))
                direct = 1;
-       } else {
-               struct snd_pcm_oss_setup *setup = substream->oss.setup;
-               direct = (setup != NULL && setup->direct);
-       }
+       else
+               direct = substream->oss.setup.direct;
 
        _snd_pcm_hw_params_any(sparams);
        _snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS);
@@ -482,7 +477,7 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
                1 : runtime->period_size;
        sw_params->xfer_align = 1;
        if (atomic_read(&runtime->mmap_count) ||
-           (substream->oss.setup && substream->oss.setup->nosilence)) {
+           substream->oss.setup.nosilence) {
                sw_params->silence_threshold = 0;
                sw_params->silence_size = 0;
        } else {
@@ -843,7 +838,7 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
                        buf += tmp;
                        bytes -= tmp;
                        xfer += tmp;
-                       if ((substream->oss.setup != NULL && substream->oss.setup->partialfrag) ||
+                       if (substream->oss.setup.partialfrag ||
                            runtime->oss.buffer_used == runtime->oss.period_bytes) {
                                tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr, 
                                                         runtime->oss.buffer_used - runtime->oss.period_ptr, 1);
@@ -959,12 +954,12 @@ static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file)
 
        substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
        if (substream != NULL) {
-               snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+               snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
                substream->runtime->oss.prepare = 1;
        }
        substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
        if (substream != NULL) {
-               snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+               snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
                substream->runtime->oss.prepare = 1;
        }
        return 0;
@@ -979,7 +974,7 @@ static int snd_pcm_oss_post(struct snd_pcm_oss_file *pcm_oss_file)
        if (substream != NULL) {
                if ((err = snd_pcm_oss_make_ready(substream)) < 0)
                        return err;
-               snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
+               snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
        }
        /* note: all errors from the start action are ignored */
        /* OSS apps do not know, how to handle them */
@@ -1108,7 +1103,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
              __direct:
                saved_f_flags = substream->ffile->f_flags;
                substream->ffile->f_flags &= ~O_NONBLOCK;
-               err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
+               err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
                substream->ffile->f_flags = saved_f_flags;
                if (err < 0)
                        return err;
@@ -1120,7 +1115,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
                if ((err = snd_pcm_oss_make_ready(substream)) < 0)
                        return err;
                runtime = substream->runtime;
-               err = snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+               err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
                if (err < 0)
                        return err;
                runtime->oss.buffer_used = 0;
@@ -1214,12 +1209,10 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
 
        if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
                return err;
-       if (atomic_read(&substream->runtime->mmap_count)) {
+       if (atomic_read(&substream->runtime->mmap_count))
                direct = 1;
-       } else {
-               struct snd_pcm_oss_setup *setup = substream->oss.setup;
-               direct = (setup != NULL && setup->direct);
-       }
+       else
+               direct = substream->oss.setup.direct;
        if (!direct)
                return AFMT_MU_LAW | AFMT_U8 |
                       AFMT_S16_LE | AFMT_S16_BE |
@@ -1437,7 +1430,7 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
                        cmd = SNDRV_PCM_IOCTL_DROP;
                        runtime->oss.prepare = 1;
                }
-               err = snd_pcm_kernel_playback_ioctl(psubstream, cmd, NULL);
+               err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
                if (err < 0)
                        return err;
        }
@@ -1458,7 +1451,7 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
                        cmd = SNDRV_PCM_IOCTL_DROP;
                        runtime->oss.prepare = 1;
                }
-               err = snd_pcm_kernel_capture_ioctl(csubstream, cmd, NULL);
+               err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
                if (err < 0)
                        return err;
        }
@@ -1495,7 +1488,7 @@ static int snd_pcm_oss_get_odelay(struct snd_pcm_oss_file *pcm_oss_file)
        runtime = substream->runtime;
        if (runtime->oss.params || runtime->oss.prepare)
                return 0;
-       err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
+       err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
        if (err == -EPIPE)
                delay = 0;      /* hack for broken OSS applications */
        else if (err < 0)
@@ -1555,8 +1548,7 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream
        } else {
                delay = snd_pcm_oss_bytes(substream, delay);
                if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-                       struct snd_pcm_oss_setup *setup = substream->oss.setup;
-                       if (setup && setup->buggyptr)
+                       if (substream->oss.setup.buggyptr)
                                info.blocks = (runtime->oss.buffer_bytes - delay - fixup) / runtime->oss.period_bytes;
                        else
                                info.blocks = (delay + fixup) / runtime->oss.period_bytes;
@@ -1638,37 +1630,46 @@ static int snd_pcm_oss_get_mapbuf(struct snd_pcm_oss_file *pcm_oss_file, int str
        return -EINVAL;
 }
 
-static struct snd_pcm_oss_setup *snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream, const char *task_name)
+static const char *strip_task_path(const char *path)
 {
-       const char *ptr, *ptrl;
-       struct snd_pcm_oss_setup *setup;
-
-       mutex_lock(&pcm->streams[stream].oss.setup_mutex);
-       for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
-               if (!strcmp(setup->task_name, task_name)) {
-                       mutex_unlock(&pcm->streams[stream].oss.setup_mutex);
-                       return setup;
-               }
-       }
-       ptr = ptrl = task_name;
-       while (*ptr) {
+       const char *ptr, *ptrl = NULL;
+       for (ptr = path; *ptr; ptr++) {
                if (*ptr == '/')
                        ptrl = ptr + 1;
-               ptr++;
        }
-       if (ptrl == task_name) {
-               goto __not_found;
-               return NULL;
-       }
-       for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
-               if (!strcmp(setup->task_name, ptrl)) {
-                       mutex_unlock(&pcm->streams[stream].oss.setup_mutex);
-                       return setup;
+       return ptrl;
+}
+
+static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
+                                     const char *task_name,
+                                     struct snd_pcm_oss_setup *rsetup)
+{
+       struct snd_pcm_oss_setup *setup;
+
+       mutex_lock(&pcm->streams[stream].oss.setup_mutex);
+       do {
+               for (setup = pcm->streams[stream].oss.setup_list; setup;
+                    setup = setup->next) {
+                       if (!strcmp(setup->task_name, task_name))
+                               goto out;
                }
-       }
-      __not_found:
+       } while ((task_name = strip_task_path(task_name)) != NULL);
+ out:
+       if (setup)
+               *rsetup = *setup;
        mutex_unlock(&pcm->streams[stream].oss.setup_mutex);
-       return NULL;
+}
+
+static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime;
+       runtime = substream->runtime;
+       vfree(runtime->oss.buffer);
+       runtime->oss.buffer = NULL;
+#ifdef CONFIG_SND_PCM_OSS_PLUGINS
+       snd_pcm_oss_plugin_clear(substream);
+#endif
+       substream->oss.oss = 0;
 }
 
 static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
@@ -1678,7 +1679,11 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
        struct snd_pcm_runtime *runtime;
 
        substream->oss.oss = 1;
-       substream->oss.setup = setup;
+       substream->oss.setup = *setup;
+       if (setup->nonblock)
+               substream->ffile->f_flags |= O_NONBLOCK;
+       else
+               substream->ffile->f_flags &= ~O_NONBLOCK;
        runtime = substream->runtime;
        runtime->oss.params = 1;
        runtime->oss.trigger = 1;
@@ -1697,18 +1702,7 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
        runtime->oss.fragshift = 0;
        runtime->oss.maxfrags = 0;
        runtime->oss.subdivision = 0;
-}
-
-static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime;
-       runtime = substream->runtime;
-       vfree(runtime->oss.buffer);
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-       snd_pcm_oss_plugin_clear(substream);
-#endif
-       substream->oss.file = NULL;
-       substream->oss.oss = 0;
+       substream->pcm_release = snd_pcm_oss_release_substream;
 }
 
 static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
@@ -1717,23 +1711,8 @@ static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
        snd_assert(pcm_oss_file != NULL, return -ENXIO);
        for (cidx = 0; cidx < 2; ++cidx) {
                struct snd_pcm_substream *substream = pcm_oss_file->streams[cidx];
-               struct snd_pcm_runtime *runtime;
-               if (substream == NULL)
-                       continue;
-               runtime = substream->runtime;
-               
-               snd_pcm_stream_lock_irq(substream);
-               if (snd_pcm_running(substream))
-                       snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
-               snd_pcm_stream_unlock_irq(substream);
-               if (substream->ffile != NULL) {
-                       if (substream->ops->hw_free != NULL)
-                               substream->ops->hw_free(substream);
-                       substream->ops->close(substream);
-                       substream->ffile = NULL;
-               }
-               snd_pcm_oss_release_substream(substream);
-               snd_pcm_release_substream(substream);
+               if (substream)
+                       snd_pcm_release_substream(substream);
        }
        kfree(pcm_oss_file);
        return 0;
@@ -1743,12 +1722,11 @@ static int snd_pcm_oss_open_file(struct file *file,
                                 struct snd_pcm *pcm,
                                 struct snd_pcm_oss_file **rpcm_oss_file,
                                 int minor,
-                                struct snd_pcm_oss_setup *psetup,
-                                struct snd_pcm_oss_setup *csetup)
+                                struct snd_pcm_oss_setup *setup)
 {
-       int err = 0;
+       int idx, err;
        struct snd_pcm_oss_file *pcm_oss_file;
-       struct snd_pcm_substream *psubstream = NULL, *csubstream = NULL;
+       struct snd_pcm_substream *substream;
        unsigned int f_mode = file->f_mode;
 
        snd_assert(rpcm_oss_file != NULL, return -EINVAL);
@@ -1761,73 +1739,31 @@ static int snd_pcm_oss_open_file(struct file *file,
        if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) &&
            (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX))
                f_mode = FMODE_WRITE;
-       if ((f_mode & FMODE_WRITE) && !(psetup && psetup->disable)) {
-               if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
-                                              &psubstream)) < 0) {
+
+       for (idx = 0; idx < 2; idx++) {
+               if (setup[idx].disable)
+                       continue;
+               if (idx == SNDRV_PCM_STREAM_PLAYBACK) {
+                       if (! (f_mode & FMODE_WRITE))
+                               continue;
+               } else {
+                       if (! (f_mode & FMODE_READ))
+                               continue;
+               }
+               err = snd_pcm_open_substream(pcm, idx, file, &substream);
+               if (err < 0) {
                        snd_pcm_oss_release_file(pcm_oss_file);
                        return err;
                }
-               pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK] = psubstream;
-       }
-       if ((f_mode & FMODE_READ) && !(csetup && csetup->disable)) {
-               if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE, 
-                                              &csubstream)) < 0) {
-                       if (!(f_mode & FMODE_WRITE) || err != -ENODEV) {
-                               snd_pcm_oss_release_file(pcm_oss_file);
-                               return err;
-                       } else {
-                               csubstream = NULL;
-                       }
-               }
-               pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE] = csubstream;
+
+               pcm_oss_file->streams[idx] = substream;
+               snd_pcm_oss_init_substream(substream, &setup[idx], minor);
        }
        
-       if (psubstream == NULL && csubstream == NULL) {
+       if (! pcm_oss_file->streams[0] && pcm_oss_file->streams[1]) {
                snd_pcm_oss_release_file(pcm_oss_file);
                return -EINVAL;
        }
-       if (psubstream != NULL) {
-               psubstream->oss.file = pcm_oss_file;
-               err = snd_pcm_hw_constraints_init(psubstream);
-               if (err < 0) {
-                       snd_printd("snd_pcm_hw_constraint_init failed\n");
-                       snd_pcm_oss_release_file(pcm_oss_file);
-                       return err;
-               }
-               if ((err = psubstream->ops->open(psubstream)) < 0) {
-                       snd_pcm_oss_release_file(pcm_oss_file);
-                       return err;
-               }
-               psubstream->ffile = file;
-               err = snd_pcm_hw_constraints_complete(psubstream);
-               if (err < 0) {
-                       snd_printd("snd_pcm_hw_constraint_complete failed\n");
-                       snd_pcm_oss_release_file(pcm_oss_file);
-                       return err;
-               }
-               snd_pcm_oss_init_substream(psubstream, psetup, minor);
-       }
-       if (csubstream != NULL) {
-               csubstream->oss.file = pcm_oss_file;
-               err = snd_pcm_hw_constraints_init(csubstream);
-               if (err < 0) {
-                       snd_printd("snd_pcm_hw_constraint_init failed\n");
-                       snd_pcm_oss_release_file(pcm_oss_file);
-                       return err;
-               }
-               if ((err = csubstream->ops->open(csubstream)) < 0) {
-                       snd_pcm_oss_release_file(pcm_oss_file);
-                       return err;
-               }
-               csubstream->ffile = file;
-               err = snd_pcm_hw_constraints_complete(csubstream);
-               if (err < 0) {
-                       snd_printd("snd_pcm_hw_constraint_complete failed\n");
-                       snd_pcm_oss_release_file(pcm_oss_file);
-                       return err;
-               }
-               snd_pcm_oss_init_substream(csubstream, csetup, minor);
-       }
 
        file->private_data = pcm_oss_file;
        *rpcm_oss_file = pcm_oss_file;
@@ -1852,7 +1788,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
        char task_name[32];
        struct snd_pcm *pcm;
        struct snd_pcm_oss_file *pcm_oss_file;
-       struct snd_pcm_oss_setup *psetup = NULL, *csetup = NULL;
+       struct snd_pcm_oss_setup setup[2];
        int nonblock;
        wait_queue_t wait;
 
@@ -1873,23 +1809,15 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
                err = -EFAULT;
                goto __error;
        }
+       memset(setup, 0, sizeof(*setup));
        if (file->f_mode & FMODE_WRITE)
-               psetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK, task_name);
+               snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                                          task_name, &setup[0]);
        if (file->f_mode & FMODE_READ)
-               csetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE, task_name);
+               snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                                          task_name, &setup[1]);
 
        nonblock = !!(file->f_flags & O_NONBLOCK);
-       if (psetup && !psetup->disable) {
-               if (psetup->nonblock)
-                       nonblock = 1;
-               else if (psetup->block)
-                       nonblock = 0;
-       } else if (csetup && !csetup->disable) {
-               if (csetup->nonblock)
-                       nonblock = 1;
-               else if (csetup->block)
-                       nonblock = 0;
-       }
        if (!nonblock)
                nonblock = nonblock_open;
 
@@ -1898,7 +1826,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
        mutex_lock(&pcm->open_mutex);
        while (1) {
                err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file,
-                                           iminor(inode), psetup, csetup);
+                                           iminor(inode), setup);
                if (err >= 0)
                        break;
                if (err == -EAGAIN) {
@@ -2312,13 +2240,8 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry,
 
 static void snd_pcm_oss_proc_free_setup_list(struct snd_pcm_str * pstr)
 {
-       unsigned int idx;
-       struct snd_pcm_substream *substream;
        struct snd_pcm_oss_setup *setup, *setupn;
 
-       for (idx = 0, substream = pstr->substream;
-            idx < pstr->substream_count; idx++, substream = substream->next)
-               substream->oss.setup = NULL;
        for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL;
             setup; setup = setupn) {
                setupn = setup->next;
@@ -2379,21 +2302,28 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
                        }
                } while (*str);
                if (setup == NULL) {
-                       setup = kmalloc(sizeof(struct snd_pcm_oss_setup), GFP_KERNEL);
-                       if (setup) {
-                               if (pstr->oss.setup_list == NULL) {
-                                       pstr->oss.setup_list = setup;
-                               } else {
-                                       for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next);
-                                       setup1->next = setup;
-                               }
-                               template.task_name = kstrdup(task_name, GFP_KERNEL);
-                       } else {
+                       setup = kmalloc(sizeof(*setup), GFP_KERNEL);
+                       if (! setup) {
+                               buffer->error = -ENOMEM;
+                               mutex_lock(&pstr->oss.setup_mutex);
+                               return;
+                       }
+                       if (pstr->oss.setup_list == NULL)
+                               pstr->oss.setup_list = setup;
+                       else {
+                               for (setup1 = pstr->oss.setup_list;
+                                    setup1->next; setup1 = setup1->next);
+                               setup1->next = setup;
+                       }
+                       template.task_name = kstrdup(task_name, GFP_KERNEL);
+                       if (! template.task_name) {
+                               kfree(setup);
                                buffer->error = -ENOMEM;
+                               mutex_lock(&pstr->oss.setup_mutex);
+                               return;
                        }
                }
-               if (setup)
-                       *setup = template;
+               *setup = template;
                mutex_unlock(&pstr->oss.setup_mutex);
        }
 }
index 3da6a38..5d7eb12 100644 (file)
@@ -777,8 +777,9 @@ static void snd_pcm_tick_timer_func(unsigned long data)
        snd_pcm_tick_elapsed(substream);
 }
 
-int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
-                          struct snd_pcm_substream **rsubstream)
+int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
+                            struct file *file,
+                            struct snd_pcm_substream **rsubstream)
 {
        struct snd_pcm_str * pstr;
        struct snd_pcm_substream *substream;
@@ -793,7 +794,7 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
        *rsubstream = NULL;
        snd_assert(pcm != NULL, return -ENXIO);
        pstr = &pcm->streams[stream];
-       if (pstr->substream == NULL)
+       if (pstr->substream == NULL || pstr->substream_count == 0)
                return -ENODEV;
 
        card = pcm->card;
@@ -807,8 +808,6 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
        }
        up_read(&card->controls_rwsem);
 
-       if (pstr->substream_count == 0)
-               return -ENODEV;
        switch (stream) {
        case SNDRV_PCM_STREAM_PLAYBACK:
                if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
@@ -874,12 +873,13 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
 
        substream->runtime = runtime;
        substream->private_data = pcm->private_data;
+       substream->ffile = file;
        pstr->substream_opened++;
        *rsubstream = substream;
        return 0;
 }
 
-void snd_pcm_release_substream(struct snd_pcm_substream *substream)
+void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime;
        substream->file = NULL;
@@ -1111,8 +1111,6 @@ EXPORT_SYMBOL(snd_pcm_link_rwlock);
 EXPORT_SYMBOL(snd_pcm_suspend);
 EXPORT_SYMBOL(snd_pcm_suspend_all);
 #endif
-EXPORT_SYMBOL(snd_pcm_kernel_playback_ioctl);
-EXPORT_SYMBOL(snd_pcm_kernel_capture_ioctl);
 EXPORT_SYMBOL(snd_pcm_kernel_ioctl);
 EXPORT_SYMBOL(snd_pcm_mmap_data);
 #if SNDRV_PCM_INFO_MMAP_IOMEM
index eeba2f0..230a940 100644 (file)
@@ -2299,19 +2299,7 @@ snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const v
        if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
                return -EBADFD;
 
-       snd_assert(substream->ffile != NULL, return -ENXIO);
        nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
-       if (substream->oss.oss) {
-               struct snd_pcm_oss_setup *setup = substream->oss.setup;
-               if (setup != NULL) {
-                       if (setup->nonblock)
-                               nonblock = 1;
-                       else if (setup->block)
-                               nonblock = 0;
-               }
-       }
-#endif
 
        if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
            runtime->channels > 1)
@@ -2374,19 +2362,7 @@ snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
        if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
                return -EBADFD;
 
-       snd_assert(substream->ffile != NULL, return -ENXIO);
        nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
-       if (substream->oss.oss) {
-               struct snd_pcm_oss_setup *setup = substream->oss.setup;
-               if (setup != NULL) {
-                       if (setup->nonblock)
-                               nonblock = 1;
-                       else if (setup->block)
-                               nonblock = 0;
-               }
-       }
-#endif
 
        if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
                return -EINVAL;
@@ -2596,19 +2572,7 @@ snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __u
        if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
                return -EBADFD;
 
-       snd_assert(substream->ffile != NULL, return -ENXIO);
        nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
-       if (substream->oss.oss) {
-               struct snd_pcm_oss_setup *setup = substream->oss.setup;
-               if (setup != NULL) {
-                       if (setup->nonblock)
-                               nonblock = 1;
-                       else if (setup->block)
-                               nonblock = 0;
-               }
-       }
-#endif
        if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
                return -EINVAL;
        return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
@@ -2665,20 +2629,7 @@ snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
        if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
                return -EBADFD;
 
-       snd_assert(substream->ffile != NULL, return -ENXIO);
        nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
-       if (substream->oss.oss) {
-               struct snd_pcm_oss_setup *setup = substream->oss.setup;
-               if (setup != NULL) {
-                       if (setup->nonblock)
-                               nonblock = 1;
-                       else if (setup->block)
-                               nonblock = 0;
-               }
-       }
-#endif
-
        if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
                return -EINVAL;
        return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer);
index 01f150f..964e4c4 100644 (file)
@@ -1170,7 +1170,7 @@ static int snd_pcm_resume(struct snd_pcm_substream *substream)
        int res;
 
        snd_power_lock(card);
-       if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)
+       if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
                res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0);
        snd_power_unlock(card);
        return res;
@@ -1198,7 +1198,7 @@ static int snd_pcm_xrun(struct snd_pcm_substream *substream)
 
        snd_power_lock(card);
        if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
-               result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+               result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
                if (result < 0)
                        goto _unlock;
        }
@@ -1313,13 +1313,13 @@ static struct action_ops snd_pcm_action_prepare = {
  *
  * Prepare the PCM substream to be triggerable.
  */
-int snd_pcm_prepare(struct snd_pcm_substream *substream)
+static int snd_pcm_prepare(struct snd_pcm_substream *substream)
 {
        int res;
        struct snd_card *card = substream->pcm->card;
 
        snd_power_lock(card);
-       if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)
+       if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
                res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, 0);
        snd_power_unlock(card);
        return res;
@@ -1410,7 +1410,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
 
        snd_power_lock(card);
        if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
-               result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+               result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
                if (result < 0) {
                        snd_power_unlock(card);
                        return result;
@@ -1533,7 +1533,7 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
 
        snd_power_lock(card);
        if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
-               result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+               result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
                if (result < 0)
                        goto _unlock;
        }
@@ -1995,28 +1995,63 @@ static void snd_pcm_remove_file(struct snd_pcm_str *str,
        }
 }
 
-static int snd_pcm_release_file(struct snd_pcm_file * pcm_file)
+static void pcm_release_private(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_substream *substream;
-       struct snd_pcm_runtime *runtime;
-       struct snd_pcm_str * str;
+       struct snd_pcm_file *pcm_file = substream->file;
 
-       snd_assert(pcm_file != NULL, return -ENXIO);
-       substream = pcm_file->substream;
-       snd_assert(substream != NULL, return -ENXIO);
-       runtime = substream->runtime;
-       str = substream->pstr;
        snd_pcm_unlink(substream);
-       if (substream->ffile != NULL) {
+       snd_pcm_remove_file(substream->pstr, pcm_file);
+       kfree(pcm_file);
+}
+
+void snd_pcm_release_substream(struct snd_pcm_substream *substream)
+{
+       snd_pcm_drop(substream);
+       if (substream->pcm_release)
+               substream->pcm_release(substream);
+       if (substream->hw_opened) {
                if (substream->ops->hw_free != NULL)
                        substream->ops->hw_free(substream);
                substream->ops->close(substream);
-               substream->ffile = NULL;
+               substream->hw_opened = 0;
        }
-       snd_pcm_remove_file(str, pcm_file);
-       snd_pcm_release_substream(substream);
-       kfree(pcm_file);
+       snd_pcm_detach_substream(substream);
+}
+
+int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
+                          struct file *file,
+                          struct snd_pcm_substream **rsubstream)
+{
+       struct snd_pcm_substream *substream;
+       int err;
+
+       err = snd_pcm_attach_substream(pcm, stream, file, &substream);
+       if (err < 0)
+               return err;
+       substream->no_mmap_ctrl = 0;
+       err = snd_pcm_hw_constraints_init(substream);
+       if (err < 0) {
+               snd_printd("snd_pcm_hw_constraints_init failed\n");
+               goto error;
+       }
+
+       if ((err = substream->ops->open(substream)) < 0)
+               goto error;
+
+       substream->hw_opened = 1;
+
+       err = snd_pcm_hw_constraints_complete(substream);
+       if (err < 0) {
+               snd_printd("snd_pcm_hw_constraints_complete failed\n");
+               goto error;
+       }
+
+       *rsubstream = substream;
        return 0;
+
+ error:
+       snd_pcm_release_substream(substream);
+       return err;
 }
 
 static int snd_pcm_open_file(struct file *file,
@@ -2024,52 +2059,29 @@ static int snd_pcm_open_file(struct file *file,
                             int stream,
                             struct snd_pcm_file **rpcm_file)
 {
-       int err = 0;
        struct snd_pcm_file *pcm_file;
        struct snd_pcm_substream *substream;
        struct snd_pcm_str *str;
+       int err;
 
        snd_assert(rpcm_file != NULL, return -EINVAL);
        *rpcm_file = NULL;
 
+       err = snd_pcm_open_substream(pcm, stream, file, &substream);
+       if (err < 0)
+               return err;
+
        pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
        if (pcm_file == NULL) {
+               snd_pcm_release_substream(substream);
                return -ENOMEM;
        }
-
-       if ((err = snd_pcm_open_substream(pcm, stream, &substream)) < 0) {
-               kfree(pcm_file);
-               return err;
-       }
-
        str = substream->pstr;
        substream->file = pcm_file;
-       substream->no_mmap_ctrl = 0;
-
+       substream->pcm_release = pcm_release_private;
        pcm_file->substream = substream;
-
        snd_pcm_add_file(str, pcm_file);
 
-       err = snd_pcm_hw_constraints_init(substream);
-       if (err < 0) {
-               snd_printd("snd_pcm_hw_constraints_init failed\n");
-               snd_pcm_release_file(pcm_file);
-               return err;
-       }
-
-       if ((err = substream->ops->open(substream)) < 0) {
-               snd_pcm_release_file(pcm_file);
-               return err;
-       }
-       substream->ffile = file;
-
-       err = snd_pcm_hw_constraints_complete(substream);
-       if (err < 0) {
-               snd_printd("snd_pcm_hw_constraints_complete failed\n");
-               snd_pcm_release_file(pcm_file);
-               return err;
-       }
-
        file->private_data = pcm_file;
        *rpcm_file = pcm_file;
        return 0;
@@ -2158,10 +2170,9 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
        snd_assert(substream != NULL, return -ENXIO);
        snd_assert(!atomic_read(&substream->runtime->mmap_count), );
        pcm = substream->pcm;
-       snd_pcm_drop(substream);
        fasync_helper(-1, file, 0, &substream->runtime->fasync);
        mutex_lock(&pcm->open_mutex);
-       snd_pcm_release_file(pcm_file);
+       snd_pcm_release_substream(substream);
        mutex_unlock(&pcm->open_mutex);
        wake_up(&pcm->open_wait);
        module_put(pcm->card->module);
@@ -2480,11 +2491,6 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
        return 0;
 }
                
-static int snd_pcm_playback_ioctl1(struct snd_pcm_substream *substream,
-                                  unsigned int cmd, void __user *arg);
-static int snd_pcm_capture_ioctl1(struct snd_pcm_substream *substream,
-                                 unsigned int cmd, void __user *arg);
-
 static int snd_pcm_common_ioctl1(struct snd_pcm_substream *substream,
                                 unsigned int cmd, void __user *arg)
 {
@@ -2736,41 +2742,28 @@ static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,
        return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void __user *)arg);
 }
 
-int snd_pcm_kernel_playback_ioctl(struct snd_pcm_substream *substream,
-                                 unsigned int cmd, void *arg)
-{
-       mm_segment_t fs;
-       int result;
-       
-       fs = snd_enter_user();
-       result = snd_pcm_playback_ioctl1(substream, cmd, (void __user *)arg);
-       snd_leave_user(fs);
-       return result;
-}
-
-int snd_pcm_kernel_capture_ioctl(struct snd_pcm_substream *substream,
-                                unsigned int cmd, void *arg)
+int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
+                        unsigned int cmd, void *arg)
 {
        mm_segment_t fs;
        int result;
        
        fs = snd_enter_user();
-       result = snd_pcm_capture_ioctl1(substream, cmd, (void __user *)arg);
-       snd_leave_user(fs);
-       return result;
-}
-
-int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
-                        unsigned int cmd, void *arg)
-{
        switch (substream->stream) {
        case SNDRV_PCM_STREAM_PLAYBACK:
-               return snd_pcm_kernel_playback_ioctl(substream, cmd, arg);
+               result = snd_pcm_playback_ioctl1(substream,
+                                                cmd, (void __user *)arg);
+               break;
        case SNDRV_PCM_STREAM_CAPTURE:
-               return snd_pcm_kernel_capture_ioctl(substream, cmd, arg);
+               result = snd_pcm_capture_ioctl1(substream,
+                                               cmd, (void __user *)arg);
+               break;
        default:
-               return -EINVAL;
+               result = -EINVAL;
+               break;
        }
+       snd_leave_user(fs);
+       return result;
 }
 
 static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
index ff8fef9..557c4de 100644 (file)
@@ -11,6 +11,15 @@ config SND_CS4231_LIB
         tristate
         select SND_PCM
 
+config SND_ADLIB
+       tristate "AdLib FM card"
+       select SND_OPL3_LIB
+       help
+         Say Y here to include support for AdLib FM cards.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-adlib.
+
 config SND_AD1816A
        tristate "Analog Devices SoundPort AD1816A"
        depends on SND && PNP && ISA
@@ -292,6 +301,20 @@ config SND_OPTI93X
          To compile this driver as a module, choose M here: the module
          will be called snd-opti93x.
 
+config SND_MIRO
+       tristate "Miro miroSOUND PCM1pro/PCM12/PCM20radio driver"
+       depends on SND
+       select SND_OPL4_LIB
+       select SND_CS4231_LIB
+       select SND_MPU401_UART
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Miro miroSOUND PCM1 pro, 
+         miroSOUND PCM12 and miroSOUND PCM20 Radio soundcards.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-miro.
+
 config SND_SB8
        tristate "Sound Blaster 1.0/2.0/Pro (8-bit)"
        depends on SND
index 05724eb..bb317cc 100644 (file)
@@ -3,6 +3,7 @@
 # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
 #
 
+snd-adlib-objs := adlib.o
 snd-als100-objs := als100.o
 snd-azt2320-objs := azt2320.o
 snd-cmi8330-objs := cmi8330.o
@@ -13,6 +14,7 @@ snd-sgalaxy-objs := sgalaxy.o
 snd-sscape-objs := sscape.o
 
 # Toplevel Module Dependency
+obj-$(CONFIG_SND_ADLIB) += snd-adlib.o
 obj-$(CONFIG_SND_ALS100) += snd-als100.o
 obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o
 obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o
diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
new file mode 100644 (file)
index 0000000..a253a14
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * AdLib FM card driver.
+ */
+
+#include <sound/driver.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/opl3.h>
+
+#define CRD_NAME "AdLib FM"
+#define DRV_NAME "snd_adlib"
+
+MODULE_DESCRIPTION(CRD_NAME);
+MODULE_AUTHOR("Rene Herman");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
+
+static struct platform_device *devices[SNDRV_CARDS];
+
+static void snd_adlib_free(struct snd_card *card)
+{
+       release_and_free_resource(card->private_data);
+}
+
+static int __devinit snd_adlib_probe(struct platform_device *device)
+{
+       struct snd_card *card;
+       struct snd_opl3 *opl3;
+
+       int error;
+       int i = device->id;
+
+       if (port[i] == SNDRV_AUTO_PORT) {
+               snd_printk(KERN_ERR DRV_NAME ": please specify port\n");
+               error = -EINVAL;
+               goto out0;
+       }
+
+       card = snd_card_new(index[i], id[i], THIS_MODULE, 0);
+       if (!card) {
+               snd_printk(KERN_ERR DRV_NAME ": could not create card\n");
+               error = -EINVAL;
+               goto out0;
+       }
+
+       card->private_data = request_region(port[i], 4, CRD_NAME);
+       if (!card->private_data) {
+               snd_printk(KERN_ERR DRV_NAME ": could not grab ports\n");
+               error = -EBUSY;
+               goto out1;
+       }
+       card->private_free = snd_adlib_free;
+
+       error = snd_opl3_create(card, port[i], port[i] + 2, OPL3_HW_AUTO, 1, &opl3);
+       if (error < 0) {
+               snd_printk(KERN_ERR DRV_NAME ": could not create OPL\n");
+               goto out1;
+       }
+
+       error = snd_opl3_hwdep_new(opl3, 0, 0, NULL);
+       if (error < 0) {
+               snd_printk(KERN_ERR DRV_NAME ": could not create FM\n");
+               goto out1;
+       }
+
+       strcpy(card->driver, DRV_NAME);
+       strcpy(card->shortname, CRD_NAME);
+       sprintf(card->longname, CRD_NAME " at %#lx", port[i]);
+
+       snd_card_set_dev(card, &device->dev);
+
+       error = snd_card_register(card);
+       if (error < 0) {
+               snd_printk(KERN_ERR DRV_NAME ": could not register card\n");
+               goto out1;
+       }
+
+       platform_set_drvdata(device, card);
+       return 0;
+
+out1:  snd_card_free(card);
+ out0: error = -EINVAL; /* FIXME: should be the original error code */
+       return error;
+}
+
+static int __devexit snd_adlib_remove(struct platform_device *device)
+{
+       snd_card_free(platform_get_drvdata(device));
+       platform_set_drvdata(device, NULL);
+       return 0;
+}
+
+static struct platform_driver snd_adlib_driver = {
+       .probe          = snd_adlib_probe,
+       .remove         = __devexit_p(snd_adlib_remove),
+
+       .driver         = {
+               .name   = DRV_NAME
+       }
+};
+
+static int __init alsa_card_adlib_init(void)
+{
+       int i, cards;
+
+       if (platform_driver_register(&snd_adlib_driver) < 0) {
+               snd_printk(KERN_ERR DRV_NAME ": could not register driver\n");
+               return -ENODEV;
+       }
+
+       for (cards = 0, i = 0; i < SNDRV_CARDS; i++) {
+               struct platform_device *device;
+
+               if (!enable[i])
+                       continue;
+
+               device = platform_device_register_simple(DRV_NAME, i, NULL, 0);
+               if (IS_ERR(device))
+                       continue;
+
+               devices[i] = device;
+               cards++;
+       }
+
+       if (!cards) {
+#ifdef MODULE
+               printk(KERN_ERR CRD_NAME " soundcard not found or device busy\n");
+#endif
+               platform_driver_unregister(&snd_adlib_driver);
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static void __exit alsa_card_adlib_exit(void)
+{
+       int i;
+
+       for (i = 0; i < SNDRV_CARDS; i++)
+               platform_device_unregister(devices[i]);
+       platform_driver_unregister(&snd_adlib_driver);
+}
+
+module_init(alsa_card_adlib_init);
+module_exit(alsa_card_adlib_exit);
index fa63048..bc0f5eb 100644 (file)
@@ -693,9 +693,9 @@ static int __init alsa_card_cmi8330_init(void)
        if ((err = platform_driver_register(&snd_cmi8330_driver)) < 0)
                return err;
 
-       for (i = 0; i < SNDRV_CARDS && enable[i]; i++) {
+       for (i = 0; i < SNDRV_CARDS; i++) {
                struct platform_device *device;
-               if (is_isapnp_selected(i))
+               if (! enable[i] || is_isapnp_selected(i))
                        continue;
                device = platform_device_register_simple(CMI8330_DRIVER,
                                                         i, NULL, 0);
index 28c6407..0e41bfd 100644 (file)
@@ -6,8 +6,10 @@
 snd-opti92x-ad1848-objs := opti92x-ad1848.o
 snd-opti92x-cs4231-objs := opti92x-cs4231.o
 snd-opti93x-objs := opti93x.o
+snd-miro-objs := miro.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o
 obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opti92x-cs4231.o
 obj-$(CONFIG_SND_OPTI93X) += snd-opti93x.o
+obj-$(CONFIG_SND_MIRO) += snd-miro.o
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
new file mode 100644 (file)
index 0000000..09384d0
--- /dev/null
@@ -0,0 +1,1455 @@
+/*
+ *   ALSA soundcard driver for Miro miroSOUND PCM1 pro
+ *                                  miroSOUND PCM12
+ *                                  miroSOUND PCM20 Radio
+ *
+ *   Copyright (C) 2004-2005 Martin Langer <martin-langer@gmx.de>
+ *
+ *   Based on OSS ACI and ALSA OPTi9xx drivers
+ *
+ *   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 <sound/driver.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/moduleparam.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <sound/core.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#include <sound/opl4.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#include <sound/initval.h>
+#include "miro.h"
+
+MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Miro miroSOUND PCM1 pro, PCM12, PCM20 Radio");
+MODULE_SUPPORTED_DEVICE("{{Miro,miroSOUND PCM1 pro}, "
+                       "{Miro,miroSOUND PCM12}, "
+                       "{Miro,miroSOUND PCM20 Radio}}");
+
+static int index = SNDRV_DEFAULT_IDX1;         /* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1;          /* ID for this card */
+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 */
+static int irq = SNDRV_DEFAULT_IRQ1;           /* 5,7,9,10,11 */
+static int mpu_irq = SNDRV_DEFAULT_IRQ1;       /* 5,7,9,10 */
+static int dma1 = SNDRV_DEFAULT_DMA1;          /* 0,1,3 */
+static int dma2 = SNDRV_DEFAULT_DMA1;          /* 0,1,3 */
+static int wss;
+static int ide;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for miro soundcard.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for miro soundcard.");
+module_param(port, long, 0444);
+MODULE_PARM_DESC(port, "WSS port # for miro driver.");
+module_param(mpu_port, long, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 port # for miro driver.");
+module_param(fm_port, long, 0444);
+MODULE_PARM_DESC(fm_port, "FM Port # for miro driver.");
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "WSS irq # for miro driver.");
+module_param(mpu_irq, int, 0444);
+MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for miro driver.");
+module_param(dma1, int, 0444);
+MODULE_PARM_DESC(dma1, "1st dma # for miro driver.");
+module_param(dma2, int, 0444);
+MODULE_PARM_DESC(dma2, "2nd dma # for miro driver.");
+module_param(wss, int, 0444);
+MODULE_PARM_DESC(wss, "wss mode");
+module_param(ide, int, 0444);
+MODULE_PARM_DESC(ide, "enable ide port");
+
+#define OPTi9XX_HW_DETECT      0
+#define OPTi9XX_HW_82C928      1
+#define OPTi9XX_HW_82C929      2
+#define OPTi9XX_HW_82C924      3
+#define OPTi9XX_HW_82C925      4
+#define OPTi9XX_HW_82C930      5
+#define OPTi9XX_HW_82C931      6
+#define OPTi9XX_HW_82C933      7
+#define OPTi9XX_HW_LAST                OPTi9XX_HW_82C933
+
+#define OPTi9XX_MC_REG(n)      n
+
+
+struct snd_miro {
+       unsigned short hardware;
+       unsigned char password;
+       char name[7];
+
+       struct resource *res_mc_base;
+       struct resource *res_aci_port;
+
+       unsigned long mc_base;
+       unsigned long mc_base_size;
+       unsigned long pwd_reg;
+
+       spinlock_t lock;
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+
+       long wss_base;
+       int irq;
+       int dma1;
+       int dma2;
+
+       long fm_port;
+
+       long mpu_port;
+       int mpu_irq;
+
+       unsigned long aci_port;
+       int aci_vendor;
+       int aci_product;
+       int aci_version;
+       int aci_amp;
+       int aci_preamp;
+       int aci_solomode;
+
+       struct mutex aci_mutex;
+};
+
+static void snd_miro_proc_init(struct snd_miro * miro);
+
+#define DRIVER_NAME "snd-miro"
+
+static struct platform_device *device;
+
+static char * snd_opti9xx_names[] = {
+       "unkown",
+       "82C928", "82C929",
+       "82C924", "82C925",
+       "82C930", "82C931", "82C933"
+};
+
+/* 
+ *  ACI control
+ */
+
+static int aci_busy_wait(struct snd_miro * miro)
+{
+       long timeout;
+       unsigned char byte;
+
+       for (timeout = 1; timeout <= ACI_MINTIME+30; timeout++) {
+               if (((byte=inb(miro->aci_port + ACI_REG_BUSY)) & 1) == 0) {
+                       if (timeout >= ACI_MINTIME)
+                               snd_printd("aci ready in round %ld.\n",
+                                          timeout-ACI_MINTIME);
+                       return byte;
+               }
+               if (timeout >= ACI_MINTIME) {
+                       long out=10*HZ;
+                       switch (timeout-ACI_MINTIME) {
+                       case 0 ... 9:
+                               out /= 10;
+                       case 10 ... 19:
+                               out /= 10;
+                       case 20 ... 30:
+                               out /= 10;
+                       default:
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+                               schedule_timeout(out);
+                               break;
+                       }
+               }
+       }
+       snd_printk(KERN_ERR "aci_busy_wait() time out\n");
+       return -EBUSY;
+}
+
+static inline int aci_write(struct snd_miro * miro, unsigned char byte)
+{
+       if (aci_busy_wait(miro) >= 0) {
+               outb(byte, miro->aci_port + ACI_REG_COMMAND);
+               return 0;
+       } else {
+               snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte);
+               return -EBUSY;
+       }
+}
+
+static inline int aci_read(struct snd_miro * miro)
+{
+       unsigned char byte;
+
+       if (aci_busy_wait(miro) >= 0) {
+               byte=inb(miro->aci_port + ACI_REG_STATUS);
+               return byte;
+       } else {
+               snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n");
+               return -EBUSY;
+       }
+}
+
+static int aci_cmd(struct snd_miro * miro, int write1, int write2, int write3)
+{
+       int write[] = {write1, write2, write3};
+       int value, i;
+
+       if (mutex_lock_interruptible(&miro->aci_mutex))
+               return -EINTR;
+
+       for (i=0; i<3; i++) {
+               if (write[i]< 0 || write[i] > 255)
+                       break;
+               else {
+                       value = aci_write(miro, write[i]);
+                       if (value < 0)
+                               goto out;
+               }
+       }
+
+       value = aci_read(miro);
+
+out:   mutex_unlock(&miro->aci_mutex);
+       return value;
+}
+
+static int aci_getvalue(struct snd_miro * miro, unsigned char index)
+{
+       return aci_cmd(miro, ACI_STATUS, index, -1);
+}
+
+static int aci_setvalue(struct snd_miro * miro, unsigned char index, int value)
+{
+       return aci_cmd(miro, index, value, -1);
+}
+
+/*
+ *  MIXER part
+ */
+
+static int snd_miro_info_capture(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+
+       return 0;
+}
+
+static int snd_miro_get_capture(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+       int value;
+
+       if ((value = aci_getvalue(miro, ACI_S_GENERAL)) < 0) {
+               snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n", value);
+               return value;
+       }
+
+       ucontrol->value.integer.value[0] = value & 0x20;
+
+       return 0;
+}
+
+static int snd_miro_put_capture(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+       int change, value, error;
+
+       value = !(ucontrol->value.integer.value[0]);
+
+       if ((error = aci_setvalue(miro, ACI_SET_SOLOMODE, value)) < 0) {
+               snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n", error);
+               return error;
+       }
+
+       change = (value != miro->aci_solomode);
+       miro->aci_solomode = value;
+       
+       return change;
+}
+
+static int snd_miro_info_preamp(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 3;
+
+       return 0;
+}
+
+static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+       int value;
+
+       if (miro->aci_version <= 176) {
+
+               /* 
+                  OSS says it's not readable with versions < 176.
+                  But it doesn't work on my card,
+                  which is a PCM12 with aci_version = 176.
+               */
+
+               ucontrol->value.integer.value[0] = miro->aci_preamp;
+               return 0;
+       }
+
+       if ((value = aci_getvalue(miro, ACI_GET_PREAMP)) < 0) {
+               snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n", value);
+               return value;
+       }
+       
+       ucontrol->value.integer.value[0] = value;
+
+       return 0;
+}
+
+static int snd_miro_put_preamp(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+       int error, value, change;
+
+       value = ucontrol->value.integer.value[0];
+
+       if ((error = aci_setvalue(miro, ACI_SET_PREAMP, value)) < 0) {
+               snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n", error);
+               return error;
+       }
+
+       change = (value != miro->aci_preamp);
+       miro->aci_preamp = value;
+
+       return change;
+}
+
+static int snd_miro_info_amp(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+
+       return 0;
+}
+
+static int snd_miro_get_amp(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = miro->aci_amp;
+
+       return 0;
+}
+
+static int snd_miro_put_amp(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+       int error, value, change;
+
+       value = ucontrol->value.integer.value[0];
+
+       if ((error = aci_setvalue(miro, ACI_SET_POWERAMP, value)) < 0) {
+               snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error);
+               return error;
+       }
+
+       change = (value != miro->aci_amp);
+       miro->aci_amp = value;
+
+       return change;
+}
+
+#define MIRO_DOUBLE(ctl_name, ctl_index, get_right_reg, set_right_reg) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .name = ctl_name, \
+  .index = ctl_index, \
+  .info = snd_miro_info_double, \
+  .get = snd_miro_get_double, \
+  .put = snd_miro_put_double, \
+  .private_value = get_right_reg | (set_right_reg << 8) \
+}
+
+static int snd_miro_info_double(struct snd_kcontrol *kcontrol, 
+                               struct snd_ctl_elem_info *uinfo)
+{
+       int reg = kcontrol->private_value & 0xff;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+
+       if ((reg >= ACI_GET_EQ1) && (reg <= ACI_GET_EQ7)) {
+
+               /* equalizer elements */
+
+               uinfo->value.integer.min = - 0x7f;
+               uinfo->value.integer.max = 0x7f;
+       } else {
+
+               /* non-equalizer elements */
+
+               uinfo->value.integer.min = 0;
+               uinfo->value.integer.max = 0x20;
+       }
+
+       return 0;
+}
+
+static int snd_miro_get_double(struct snd_kcontrol *kcontrol, 
+                              struct snd_ctl_elem_value *uinfo)
+{
+       struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+       int left_val, right_val;
+
+       int right_reg = kcontrol->private_value & 0xff;
+       int left_reg = right_reg + 1;
+
+       if ((right_val = aci_getvalue(miro, right_reg)) < 0) {
+               snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val);
+               return right_val;
+       }
+
+       if ((left_val = aci_getvalue(miro, left_reg)) < 0) {
+               snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val);
+               return left_val;
+       }
+
+       if ((right_reg >= ACI_GET_EQ1) && (right_reg <= ACI_GET_EQ7)) {
+
+               /* equalizer elements */
+
+               if (left_val < 0x80) {
+                       uinfo->value.integer.value[0] = left_val;
+               } else {
+                       uinfo->value.integer.value[0] = 0x80 - left_val;
+               }
+
+               if (right_val < 0x80) {
+                       uinfo->value.integer.value[1] = right_val;
+               } else {
+                       uinfo->value.integer.value[1] = 0x80 - right_val;
+               }
+
+       } else {
+
+               /* non-equalizer elements */
+
+               uinfo->value.integer.value[0] = 0x20 - left_val;
+               uinfo->value.integer.value[1] = 0x20 - right_val;
+       }
+
+       return 0;
+}
+
+static int snd_miro_put_double(struct snd_kcontrol *kcontrol, 
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+       int left, right, left_old, right_old;
+       int setreg_left, setreg_right, getreg_left, getreg_right;
+       int change, error;
+
+       left = ucontrol->value.integer.value[0];
+       right = ucontrol->value.integer.value[1];
+
+       setreg_right = (kcontrol->private_value >> 8) & 0xff;
+       if (setreg_right == ACI_SET_MASTER) {
+               setreg_left = setreg_right + 1;
+       } else {
+               setreg_left = setreg_right + 8;
+       }
+
+       getreg_right = kcontrol->private_value & 0xff;
+       getreg_left = getreg_right + 1;
+
+       if ((left_old = aci_getvalue(miro, getreg_left)) < 0) {
+               snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old);
+               return left_old;
+       }
+
+       if ((right_old = aci_getvalue(miro, getreg_right)) < 0) {
+               snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
+               return right_old;
+       }
+
+       if ((getreg_right >= ACI_GET_EQ1) && (getreg_right <= ACI_GET_EQ7)) {
+
+               /* equalizer elements */
+
+               if (left_old > 0x80) 
+                       left_old = 0x80 - left_old;
+               if (right_old > 0x80) 
+                       right_old = 0x80 - right_old;
+
+               if (left >= 0) {
+                       if ((error = aci_setvalue(miro, setreg_left, left)) < 0) {
+                               snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
+                                          left, error);
+                               return error;
+                       }
+               } else {
+                       if ((error = aci_setvalue(miro, setreg_left, 0x80 - left)) < 0) {
+                               snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
+                                          0x80 - left, error);
+                               return error;
+                       }
+               }
+
+               if (right >= 0) {
+                       if ((error = aci_setvalue(miro, setreg_right, right)) < 0) {
+                               snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
+                                          right, error);
+                               return error;
+                       }
+               } else {
+                       if ((error = aci_setvalue(miro, setreg_right, 0x80 - right)) < 0) {
+                               snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
+                                          0x80 - right, error);
+                               return error;
+                       }
+               }
+
+       } else {
+
+               /* non-equalizer elements */
+
+               left_old = 0x20 - left_old;
+               right_old = 0x20 - right_old;
+
+               if ((error = aci_setvalue(miro, setreg_left, 0x20 - left)) < 0) {
+                       snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
+                                  0x20 - left, error);
+                       return error;
+               }
+               if ((error = aci_setvalue(miro, setreg_right, 0x20 - right)) < 0) {
+                       snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
+                                  0x20 - right, error);
+                       return error;
+               }
+       }
+
+       change = (left != left_old) || (right != right_old);
+
+       return change;
+}
+
+static struct snd_kcontrol_new snd_miro_controls[] = {
+MIRO_DOUBLE("Master Playback Volume", 0, ACI_GET_MASTER, ACI_SET_MASTER),
+MIRO_DOUBLE("Mic Playback Volume", 1, ACI_GET_MIC, ACI_SET_MIC),
+MIRO_DOUBLE("Line Playback Volume", 1, ACI_GET_LINE, ACI_SET_LINE),
+MIRO_DOUBLE("CD Playback Volume", 0, ACI_GET_CD, ACI_SET_CD),
+MIRO_DOUBLE("Synth Playback Volume", 0, ACI_GET_SYNTH, ACI_SET_SYNTH),
+MIRO_DOUBLE("PCM Playback Volume", 1, ACI_GET_PCM, ACI_SET_PCM),
+MIRO_DOUBLE("Aux Playback Volume", 2, ACI_GET_LINE2, ACI_SET_LINE2),
+};
+
+/* Equalizer with seven bands (only PCM20) 
+   from -12dB up to +12dB on each band */
+static struct snd_kcontrol_new snd_miro_eq_controls[] = {
+MIRO_DOUBLE("Tone Control - 28 Hz", 0, ACI_GET_EQ1, ACI_SET_EQ1),
+MIRO_DOUBLE("Tone Control - 160 Hz", 0, ACI_GET_EQ2, ACI_SET_EQ2),
+MIRO_DOUBLE("Tone Control - 400 Hz", 0, ACI_GET_EQ3, ACI_SET_EQ3),
+MIRO_DOUBLE("Tone Control - 1 kHz", 0, ACI_GET_EQ4, ACI_SET_EQ4),
+MIRO_DOUBLE("Tone Control - 2.5 kHz", 0, ACI_GET_EQ5, ACI_SET_EQ5),
+MIRO_DOUBLE("Tone Control - 6.3 kHz", 0, ACI_GET_EQ6, ACI_SET_EQ6),
+MIRO_DOUBLE("Tone Control - 16 kHz", 0, ACI_GET_EQ7, ACI_SET_EQ7),
+};
+
+static struct snd_kcontrol_new snd_miro_radio_control[] = {
+MIRO_DOUBLE("Radio Playback Volume", 0, ACI_GET_LINE1, ACI_SET_LINE1),
+};
+
+static struct snd_kcontrol_new snd_miro_line_control[] = {
+MIRO_DOUBLE("Line Playback Volume", 2, ACI_GET_LINE1, ACI_SET_LINE1),
+};
+
+static struct snd_kcontrol_new snd_miro_preamp_control[] = {
+{
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Mic Boost",
+       .index = 1,
+       .info = snd_miro_info_preamp,
+       .get = snd_miro_get_preamp,
+       .put = snd_miro_put_preamp,
+}};
+
+static struct snd_kcontrol_new snd_miro_amp_control[] = {
+{
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Line Boost",
+       .index = 0,
+       .info = snd_miro_info_amp,
+       .get = snd_miro_get_amp,
+       .put = snd_miro_put_amp,
+}};
+
+static struct snd_kcontrol_new snd_miro_capture_control[] = {
+{
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "PCM Capture Switch",
+       .index = 0,
+       .info = snd_miro_info_capture,
+       .get = snd_miro_get_capture,
+       .put = snd_miro_put_capture,
+}};
+
+static unsigned char aci_init_values[][2] __initdata = {
+       { ACI_SET_MUTE, 0x00 },
+       { ACI_SET_POWERAMP, 0x00 },
+       { ACI_SET_PREAMP, 0x00 },
+       { ACI_SET_SOLOMODE, 0x00 },
+       { ACI_SET_MIC + 0, 0x20 },
+       { ACI_SET_MIC + 8, 0x20 },
+       { ACI_SET_LINE + 0, 0x20 },
+       { ACI_SET_LINE + 8, 0x20 },
+       { ACI_SET_CD + 0, 0x20 },
+       { ACI_SET_CD + 8, 0x20 },
+       { ACI_SET_PCM + 0, 0x20 },
+       { ACI_SET_PCM + 8, 0x20 },
+       { ACI_SET_LINE1 + 0, 0x20 },
+       { ACI_SET_LINE1 + 8, 0x20 },
+       { ACI_SET_LINE2 + 0, 0x20 },
+       { ACI_SET_LINE2 + 8, 0x20 },
+       { ACI_SET_SYNTH + 0, 0x20 },
+       { ACI_SET_SYNTH + 8, 0x20 },
+       { ACI_SET_MASTER + 0, 0x20 },
+       { ACI_SET_MASTER + 1, 0x20 },
+};
+
+static int __init snd_set_aci_init_values(struct snd_miro *miro)
+{
+       int idx, error;
+
+       /* enable WSS on PCM1 */
+
+       if ((miro->aci_product == 'A') && wss) {
+               if ((error = aci_setvalue(miro, ACI_SET_WSS, wss)) < 0) {
+                       snd_printk(KERN_ERR "enabling WSS mode failed\n");
+                       return error;
+               }
+       }
+
+       /* enable IDE port */
+
+       if (ide) {
+               if ((error = aci_setvalue(miro, ACI_SET_IDE, ide)) < 0) {
+                       snd_printk(KERN_ERR "enabling IDE port failed\n");
+                       return error;
+               }
+       }
+
+       /* set common aci values */
+
+       for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++)
+                if ((error = aci_setvalue(miro, aci_init_values[idx][0], 
+                                         aci_init_values[idx][1])) < 0) {
+                       snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 
+                                  aci_init_values[idx][0], error);
+                        return error;
+                }
+
+       miro->aci_amp = 0;
+       miro->aci_preamp = 0;
+       miro->aci_solomode = 1;
+
+       return 0;
+}
+
+static int snd_miro_mixer(struct snd_miro *miro)
+{
+       struct snd_card *card;
+       unsigned int idx;
+       int err;
+
+       snd_assert(miro != NULL && miro->card != NULL, return -EINVAL);
+
+       card = miro->card;
+
+       switch (miro->hardware) {
+       case OPTi9XX_HW_82C924:
+               strcpy(card->mixername, "ACI & OPTi924");
+               break;
+       case OPTi9XX_HW_82C929:
+               strcpy(card->mixername, "ACI & OPTi929");
+               break;
+       default:
+               snd_BUG();
+               break;
+       }
+
+       for (idx = 0; idx < ARRAY_SIZE(snd_miro_controls); idx++) {
+               if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_controls[idx], miro))) < 0)
+                       return err;
+       }
+
+       if ((miro->aci_product == 'A') || (miro->aci_product == 'B')) {
+               /* PCM1/PCM12 with power-amp and Line 2 */
+               if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro))) < 0)
+                       return err;
+               if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_amp_control[0], miro))) < 0)
+                       return err;
+       }
+
+       if ((miro->aci_product == 'B') || (miro->aci_product == 'C')) {
+               /* PCM12/PCM20 with mic-preamp */
+               if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro))) < 0)
+                       return err;
+               if (miro->aci_version >= 176)
+                       if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0)
+                               return err;
+       }
+
+       if (miro->aci_product == 'C') {
+               /* PCM20 with radio and 7 band equalizer */
+               if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro))) < 0)
+                       return err;
+               for (idx = 0; idx < ARRAY_SIZE(snd_miro_eq_controls); idx++) {
+                       if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_eq_controls[idx], miro))) < 0)
+                               return err;
+               }
+       }
+
+       return 0;
+}
+
+static long snd_legacy_find_free_ioport(long *port_table, long size)
+{
+       while (*port_table != -1) {
+               struct resource *res;
+               if ((res = request_region(*port_table, size, 
+                                         "ALSA test")) != NULL) {
+                       release_and_free_resource(res);
+                       return *port_table;
+               }
+               port_table++;
+       }
+       return -1;
+}
+
+static int __init snd_miro_init(struct snd_miro *chip, unsigned short hardware)
+{
+       static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
+
+       chip->hardware = hardware;
+       strcpy(chip->name, snd_opti9xx_names[hardware]);
+
+       chip->mc_base_size = opti9xx_mc_size[hardware];  
+
+       spin_lock_init(&chip->lock);
+
+       chip->wss_base = -1;
+       chip->irq = -1;
+       chip->dma1 = -1;
+       chip->dma2 = -1;
+       chip->fm_port = -1;
+       chip->mpu_port = -1;
+       chip->mpu_irq = -1;
+
+       switch (hardware) {
+       case OPTi9XX_HW_82C929:
+               chip->mc_base = 0xf8c;
+               chip->password = 0xe3;
+               chip->pwd_reg = 3;
+               break;
+
+       case OPTi9XX_HW_82C924:
+               chip->mc_base = 0xf8c;
+               chip->password = 0xe5;
+               chip->pwd_reg = 3;
+               break;
+
+       default:
+               snd_printk(KERN_ERR "sorry, no support for %d\n", hardware);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static unsigned char snd_miro_read(struct snd_miro *chip,
+                                  unsigned char reg)
+{
+       unsigned long flags;
+       unsigned char retval = 0xff;
+
+       spin_lock_irqsave(&chip->lock, flags);
+       outb(chip->password, chip->mc_base + chip->pwd_reg);
+
+       switch (chip->hardware) {
+       case OPTi9XX_HW_82C924:
+               if (reg > 7) {
+                       outb(reg, chip->mc_base + 8);
+                       outb(chip->password, chip->mc_base + chip->pwd_reg);
+                       retval = inb(chip->mc_base + 9);
+                       break;
+               }
+
+       case OPTi9XX_HW_82C929:
+               retval = inb(chip->mc_base + reg);
+               break;
+
+       default:
+               snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware);
+       }
+
+       spin_unlock_irqrestore(&chip->lock, flags);
+       return retval;
+}
+
+static void snd_miro_write(struct snd_miro *chip, unsigned char reg,
+                          unsigned char value)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&chip->lock, flags);
+       outb(chip->password, chip->mc_base + chip->pwd_reg);
+
+       switch (chip->hardware) {
+       case OPTi9XX_HW_82C924:
+               if (reg > 7) {
+                       outb(reg, chip->mc_base + 8);
+                       outb(chip->password, chip->mc_base + chip->pwd_reg);
+                       outb(value, chip->mc_base + 9);
+                       break;
+               }
+
+       case OPTi9XX_HW_82C929:
+               outb(value, chip->mc_base + reg);
+               break;
+
+       default:
+               snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware);
+       }
+
+       spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+#define snd_miro_write_mask(chip, reg, value, mask)    \
+       snd_miro_write(chip, reg,                       \
+               (snd_miro_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+
+/*
+ *  Proc Interface
+ */
+
+static void snd_miro_proc_read(struct snd_info_entry * entry, 
+                              struct snd_info_buffer *buffer)
+{
+       struct snd_miro *miro = (struct snd_miro *) entry->private_data;
+       char* model = "unknown";
+
+       /* miroSOUND PCM1 pro, early PCM12 */
+
+       if ((miro->hardware == OPTi9XX_HW_82C929) &&
+           (miro->aci_vendor == 'm') && 
+           (miro->aci_product == 'A')) {
+               switch(miro->aci_version) {
+               case 3:
+                       model = "miroSOUND PCM1 pro";
+                       break;
+               default:
+                       model = "miroSOUND PCM1 pro / (early) PCM12";
+                       break;
+               }
+       }
+
+       /* miroSOUND PCM12, PCM12 (Rev. E), PCM12 pnp */
+
+       if ((miro->hardware == OPTi9XX_HW_82C924) &&
+           (miro->aci_vendor == 'm') && 
+           (miro->aci_product == 'B')) {
+               switch(miro->aci_version) {
+               case 4:
+                       model = "miroSOUND PCM12";
+                       break;
+               case 176:
+                       model = "miroSOUND PCM12 (Rev. E)";
+                       break;
+               default:
+                       model = "miroSOUND PCM12 / PCM12 pnp";
+                       break;
+               }
+       }
+
+       /* miroSOUND PCM20 radio */
+
+       if ((miro->hardware == OPTi9XX_HW_82C924) &&
+           (miro->aci_vendor == 'm') && 
+           (miro->aci_product == 'C')) {
+               switch(miro->aci_version) {
+               case 7:
+                       model = "miroSOUND PCM20 radio (Rev. E)";
+                       break;
+               default:
+                       model = "miroSOUND PCM20 radio";
+                       break;
+               }
+       }
+
+       snd_iprintf(buffer, "\nGeneral information:\n");
+       snd_iprintf(buffer, "  model   : %s\n", model);
+       snd_iprintf(buffer, "  opti    : %s\n", miro->name);
+       snd_iprintf(buffer, "  codec   : %s\n", miro->pcm->name);
+       snd_iprintf(buffer, "  port    : 0x%lx\n", miro->wss_base);
+       snd_iprintf(buffer, "  irq     : %d\n", miro->irq);
+       snd_iprintf(buffer, "  dma     : %d,%d\n\n", miro->dma1, miro->dma2);
+
+       snd_iprintf(buffer, "MPU-401:\n");
+       snd_iprintf(buffer, "  port    : 0x%lx\n", miro->mpu_port);
+       snd_iprintf(buffer, "  irq     : %d\n\n", miro->mpu_irq);
+
+       snd_iprintf(buffer, "ACI information:\n");
+       snd_iprintf(buffer, "  vendor  : ");
+       switch(miro->aci_vendor) {
+       case 'm':
+               snd_iprintf(buffer, "Miro\n");
+               break;
+       default:
+               snd_iprintf(buffer, "unknown (0x%x)\n", miro->aci_vendor);
+               break;
+       }
+
+       snd_iprintf(buffer, "  product : ");
+       switch(miro->aci_product) {
+       case 'A':
+               snd_iprintf(buffer, "miroSOUND PCM1 pro / (early) PCM12\n");
+               break;
+       case 'B':
+               snd_iprintf(buffer, "miroSOUND PCM12\n");
+               break;
+       case 'C':
+               snd_iprintf(buffer, "miroSOUND PCM20 radio\n");
+               break;
+       default:
+               snd_iprintf(buffer, "unknown (0x%x)\n", miro->aci_product);
+               break;
+       }
+
+       snd_iprintf(buffer, "  firmware: %d (0x%x)\n",
+                   miro->aci_version, miro->aci_version);
+       snd_iprintf(buffer, "  port    : 0x%lx-0x%lx\n", 
+                   miro->aci_port, miro->aci_port+2);
+       snd_iprintf(buffer, "  wss     : 0x%x\n", wss);
+       snd_iprintf(buffer, "  ide     : 0x%x\n", ide);
+       snd_iprintf(buffer, "  solomode: 0x%x\n", miro->aci_solomode);
+       snd_iprintf(buffer, "  amp     : 0x%x\n", miro->aci_amp);
+       snd_iprintf(buffer, "  preamp  : 0x%x\n", miro->aci_preamp);
+}
+
+static void __init snd_miro_proc_init(struct snd_miro * miro)
+{
+       struct snd_info_entry *entry;
+
+       if (! snd_card_proc_new(miro->card, "miro", &entry))
+               snd_info_set_text_ops(entry, miro, 1024, snd_miro_proc_read);
+}
+
+/*
+ *  Init
+ */
+
+static int __init snd_miro_configure(struct snd_miro *chip)
+{
+       unsigned char wss_base_bits;
+       unsigned char irq_bits;
+       unsigned char dma_bits;
+       unsigned char mpu_port_bits = 0;
+       unsigned char mpu_irq_bits;
+       unsigned long flags;
+
+       switch (chip->hardware) {
+       case OPTi9XX_HW_82C924:
+               snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
+               snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
+               snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
+               snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
+               snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
+               break;
+       case OPTi9XX_HW_82C929:
+               /* untested init commands for OPTi929 */
+               snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
+               snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
+               snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
+               snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
+               break;
+       default:
+               snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
+               return -EINVAL;
+       }
+
+       switch (chip->wss_base) {
+       case 0x530:
+               wss_base_bits = 0x00;
+               break;
+       case 0x604:
+               wss_base_bits = 0x03;
+               break;
+       case 0xe80:
+               wss_base_bits = 0x01;
+               break;
+       case 0xf40:
+               wss_base_bits = 0x02;
+               break;
+       default:
+               snd_printk(KERN_ERR "WSS port 0x%lx not valid\n", chip->wss_base);
+               goto __skip_base;
+       }
+       snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
+
+__skip_base:
+       switch (chip->irq) {
+       case 5:
+               irq_bits = 0x05;
+               break;
+       case 7:
+               irq_bits = 0x01;
+               break;
+       case 9:
+               irq_bits = 0x02;
+               break;
+       case 10:
+               irq_bits = 0x03;
+               break;
+       case 11:
+               irq_bits = 0x04;
+               break;
+       default:
+               snd_printk(KERN_ERR "WSS irq # %d not valid\n", chip->irq);
+               goto __skip_resources;
+       }
+
+       switch (chip->dma1) {
+       case 0:
+               dma_bits = 0x01;
+               break;
+       case 1:
+               dma_bits = 0x02;
+               break;
+       case 3:
+               dma_bits = 0x03;
+               break;
+       default:
+               snd_printk(KERN_ERR "WSS dma1 # %d not valid\n", chip->dma1);
+               goto __skip_resources;
+       }
+
+       if (chip->dma1 == chip->dma2) {
+               snd_printk(KERN_ERR "don't want to share dmas\n");
+               return -EBUSY;
+       }
+
+       switch (chip->dma2) {
+       case 0:
+       case 1:
+               break;
+       default:
+               snd_printk(KERN_ERR "WSS dma2 # %d not valid\n", chip->dma2);
+               goto __skip_resources;
+       }
+       dma_bits |= 0x04;
+
+       spin_lock_irqsave(&chip->lock, flags);
+       outb(irq_bits << 3 | dma_bits, chip->wss_base);
+       spin_unlock_irqrestore(&chip->lock, flags);
+
+__skip_resources:
+       if (chip->hardware > OPTi9XX_HW_82C928) {
+               switch (chip->mpu_port) {
+               case 0:
+               case -1:
+                       break;
+               case 0x300:
+                       mpu_port_bits = 0x03;
+                       break;
+               case 0x310:
+                       mpu_port_bits = 0x02;
+                       break;
+               case 0x320:
+                       mpu_port_bits = 0x01;
+                       break;
+               case 0x330:
+                       mpu_port_bits = 0x00;
+                       break;
+               default:
+                       snd_printk(KERN_ERR "MPU-401 port 0x%lx not valid\n",
+                                  chip->mpu_port);
+                       goto __skip_mpu;
+               }
+
+               switch (chip->mpu_irq) {
+               case 5:
+                       mpu_irq_bits = 0x02;
+                       break;
+               case 7:
+                       mpu_irq_bits = 0x03;
+                       break;
+               case 9:
+                       mpu_irq_bits = 0x00;
+                       break;
+               case 10:
+                       mpu_irq_bits = 0x01;
+                       break;
+               default:
+                       snd_printk(KERN_ERR "MPU-401 irq # %d not valid\n",
+                                  chip->mpu_irq);
+                       goto __skip_mpu;
+               }
+
+               snd_miro_write_mask(chip, OPTi9XX_MC_REG(6),
+                       (chip->mpu_port <= 0) ? 0x00 :
+                               0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
+                       0xf8);
+       }
+__skip_mpu:
+
+       return 0;
+}
+
+static int __init snd_card_miro_detect(struct snd_card *card, struct snd_miro *chip)
+{
+       int i, err;
+       unsigned char value;
+
+       for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) {
+
+               if ((err = snd_miro_init(chip, i)) < 0)
+                       return err;
+
+               if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
+                       continue;
+
+               value = snd_miro_read(chip, OPTi9XX_MC_REG(1));
+               if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
+                       if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
+                               return 1;
+
+               release_and_free_resource(chip->res_mc_base);
+               chip->res_mc_base = NULL;
+
+       }
+
+       return -ENODEV;
+}
+
+static int __init snd_card_miro_aci_detect(struct snd_card *card, struct snd_miro * miro)
+{
+       unsigned char regval;
+       int i;
+
+       mutex_init(&miro->aci_mutex);
+
+       /* get ACI port from OPTi9xx MC 4 */
+
+       miro->mc_base = 0xf8c;
+       regval=inb(miro->mc_base + 4);
+       miro->aci_port = (regval & 0x10) ? 0x344: 0x354;
+
+       if ((miro->res_aci_port = request_region(miro->aci_port, 3, "miro aci")) == NULL) {
+               snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n", 
+                          miro->aci_port, miro->aci_port+2);
+               return -ENOMEM;
+       }
+
+        /* force ACI into a known state */
+       for (i = 0; i < 3; i++)
+               if (aci_cmd(miro, ACI_ERROR_OP, -1, -1) < 0) {
+                       snd_card_free(card);
+                       snd_printk(KERN_ERR "can't force aci into known state.\n");
+                       return -ENXIO;
+               }
+
+       if ((miro->aci_vendor=aci_cmd(miro, ACI_READ_IDCODE, -1, -1)) < 0 ||
+           (miro->aci_product=aci_cmd(miro, ACI_READ_IDCODE, -1, -1)) < 0) {
+               snd_card_free(card);
+               snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n", miro->aci_port);
+               return -ENXIO;
+       }
+
+       if ((miro->aci_version=aci_cmd(miro, ACI_READ_VERSION, -1, -1)) < 0) {
+               snd_card_free(card);
+               snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n", 
+                          miro->aci_port);
+               return -ENXIO;
+       }
+
+       if (aci_cmd(miro, ACI_INIT, -1, -1) < 0 ||
+           aci_cmd(miro, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
+           aci_cmd(miro, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
+               snd_printk(KERN_ERR "can't initialize aci.\n"); 
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static void snd_card_miro_free(struct snd_card *card)
+{
+       struct snd_miro *miro = card->private_data;
+        
+       release_and_free_resource(miro->res_aci_port);
+       release_and_free_resource(miro->res_mc_base);
+}
+
+static int __init snd_miro_probe(struct platform_device *devptr)
+{
+       static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
+       static long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
+       static int possible_irqs[] = {11, 9, 10, 7, -1};
+       static int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
+       static int possible_dma1s[] = {3, 1, 0, -1};
+       static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
+
+       int error;
+       struct snd_miro *miro;
+       struct snd_cs4231 *codec;
+       struct snd_timer *timer;
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       struct snd_rawmidi *rmidi;
+
+       if (!(card = snd_card_new(index, id, THIS_MODULE,
+                                 sizeof(struct snd_miro))))
+               return -ENOMEM;
+
+       card->private_free = snd_card_miro_free;
+       miro = card->private_data;
+       miro->card = card;
+
+       if ((error = snd_card_miro_aci_detect(card, miro)) < 0) {
+               snd_card_free(card);
+               snd_printk(KERN_ERR "unable to detect aci chip\n");
+               return -ENODEV;
+       }
+
+       /* init proc interface */
+       snd_miro_proc_init(miro);
+
+       if ((error = snd_card_miro_detect(card, miro)) < 0) {
+               snd_card_free(card);
+               snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
+               return -ENODEV;
+       }
+
+       if (! miro->res_mc_base &&
+           (miro->res_mc_base = request_region(miro->mc_base, miro->mc_base_size,
+                                               "miro (OPTi9xx MC)")) == NULL) {
+               snd_card_free(card);
+               snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
+               return -ENOMEM;
+       }
+
+       miro->wss_base = port;
+       miro->fm_port = fm_port;
+       miro->mpu_port = mpu_port;
+       miro->irq = irq;
+       miro->mpu_irq = mpu_irq;
+       miro->dma1 = dma1;
+       miro->dma2 = dma2;
+
+       if (miro->wss_base == SNDRV_AUTO_PORT) {
+               if ((miro->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
+                       snd_card_free(card);
+                       snd_printk(KERN_ERR "unable to find a free WSS port\n");
+                       return -EBUSY;
+               }
+       }
+
+       if (miro->mpu_port == SNDRV_AUTO_PORT) {
+               if ((miro->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
+                       snd_card_free(card);
+                       snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
+                       return -EBUSY;
+               }
+       }
+       if (miro->irq == SNDRV_AUTO_IRQ) {
+               if ((miro->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+                       snd_card_free(card);
+                       snd_printk(KERN_ERR "unable to find a free IRQ\n");
+                       return -EBUSY;
+               }
+       }
+       if (miro->mpu_irq == SNDRV_AUTO_IRQ) {
+               if ((miro->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
+                       snd_card_free(card);
+                       snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
+                       return -EBUSY;
+               }
+       }
+       if (miro->dma1 == SNDRV_AUTO_DMA) {
+               if ((miro->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
+                       snd_card_free(card);
+                       snd_printk(KERN_ERR "unable to find a free DMA1\n");
+                       return -EBUSY;
+               }
+       }
+       if (miro->dma2 == SNDRV_AUTO_DMA) {
+               if ((miro->dma2 = snd_legacy_find_free_dma(possible_dma2s[miro->dma1 % 4])) < 0) {
+                       snd_card_free(card);
+                       snd_printk(KERN_ERR "unable to find a free DMA2\n");
+                       return -EBUSY;
+               }
+       }
+
+       if ((error = snd_miro_configure(miro))) {
+               snd_card_free(card);
+               return error;
+       }
+
+       if ((error = snd_cs4231_create(card, miro->wss_base + 4, -1,
+                                      miro->irq, miro->dma1, miro->dma2,
+                                      CS4231_HW_AD1845,
+                                      0,
+                                      &codec)) < 0) {
+               snd_card_free(card);
+               return error;
+       }
+
+       if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) {
+               snd_card_free(card);
+               return error;
+       }
+       if ((error = snd_cs4231_mixer(codec)) < 0) {
+               snd_card_free(card);
+               return error;
+       }
+       if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) {
+               snd_card_free(card);
+               return error;
+       }
+
+       miro->pcm = pcm;
+
+       if ((error = snd_miro_mixer(miro)) < 0) {
+               snd_card_free(card);
+               return error;
+       }
+
+       if (miro->aci_vendor == 'm') {
+               /* It looks like a miro sound card. */
+               switch (miro->aci_product) {
+               case 'A':
+                       sprintf(card->shortname, 
+                               "miroSOUND PCM1 pro / PCM12");
+                       break;
+               case 'B':
+                       sprintf(card->shortname, 
+                               "miroSOUND PCM12");
+                       break;
+               case 'C':
+                       sprintf(card->shortname, 
+                               "miroSOUND PCM20 radio");
+                       break;
+               default:
+                       sprintf(card->shortname, 
+                               "unknown miro");
+                       snd_printk(KERN_INFO "unknown miro aci id\n");
+                       break;
+               }
+       } else {
+               snd_printk(KERN_INFO "found unsupported aci card\n");
+               sprintf(card->shortname, "unknown Cardinal Technologies");
+       }
+
+       strcpy(card->driver, "miro");
+       sprintf(card->longname, "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d",
+               card->shortname, miro->name, pcm->name, miro->wss_base + 4,
+               miro->irq, miro->dma1, miro->dma2);
+
+       if (miro->mpu_port <= 0 || miro->mpu_port == SNDRV_AUTO_PORT)
+               rmidi = NULL;
+       else
+               if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+                               miro->mpu_port, 0, miro->mpu_irq, SA_INTERRUPT,
+                               &rmidi)))
+                       snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", miro->mpu_port);
+
+       if (miro->fm_port > 0 && miro->fm_port != SNDRV_AUTO_PORT) {
+               struct snd_opl3 *opl3 = NULL;
+               struct snd_opl4 *opl4;
+               if (snd_opl4_create(card, miro->fm_port, miro->fm_port - 8, 
+                                   2, &opl3, &opl4) < 0)
+                       snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n", miro->fm_port);
+       }
+
+       if ((error = snd_set_aci_init_values(miro)) < 0) {
+               snd_card_free(card);
+                return error;
+       }
+
+       snd_card_set_dev(card, &devptr->dev);
+
+       if ((error = snd_card_register(card))) {
+               snd_card_free(card);
+               return error;
+       }
+
+       platform_set_drvdata(devptr, card);
+       return 0;
+}
+
+static int __devexit snd_miro_remove(struct platform_device *devptr)
+{
+       snd_card_free(platform_get_drvdata(devptr));
+       platform_set_drvdata(devptr, NULL);
+       return 0;
+}
+
+static struct platform_driver snd_miro_driver = {
+       .probe          = snd_miro_probe,
+       .remove         = __devexit_p(snd_miro_remove),
+       /* FIXME: suspend/resume */
+       .driver         = {
+               .name   = DRIVER_NAME
+       },
+};
+
+static int __init alsa_card_miro_init(void)
+{
+       int error;
+
+       if ((error = platform_driver_register(&snd_miro_driver)) < 0)
+               return error;
+       device = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
+       if (! IS_ERR(device))
+               return 0;
+#ifdef MODULE
+       printk(KERN_ERR "no miro soundcard found\n");
+#endif
+       platform_driver_unregister(&snd_miro_driver);
+       return PTR_ERR(device);
+}
+
+static void __exit alsa_card_miro_exit(void)
+{
+       platform_device_unregister(device);
+       platform_driver_unregister(&snd_miro_driver);
+}
+
+module_init(alsa_card_miro_init)
+module_exit(alsa_card_miro_exit)
diff --git a/sound/isa/opti9xx/miro.h b/sound/isa/opti9xx/miro.h
new file mode 100644 (file)
index 0000000..6e1385b
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef _MIRO_H_
+#define _MIRO_H_
+
+#define ACI_REG_COMMAND                0       /* write register offset */
+#define ACI_REG_STATUS         1       /* read register offset */
+#define ACI_REG_BUSY           2       /* busy register offset */
+#define ACI_REG_RDS            2       /* PCM20: RDS register offset */
+#define ACI_MINTIME            500     /* ACI time out limit */
+
+#define ACI_SET_MUTE           0x0d
+#define ACI_SET_POWERAMP       0x0f
+#define ACI_SET_TUNERMUTE      0xa3
+#define ACI_SET_TUNERMONO      0xa4
+#define ACI_SET_IDE            0xd0
+#define ACI_SET_WSS            0xd1
+#define ACI_SET_SOLOMODE       0xd2
+#define ACI_SET_PREAMP         0x03
+#define ACI_GET_PREAMP         0x21
+#define ACI_WRITE_TUNE         0xa7
+#define ACI_READ_TUNERSTEREO   0xa8
+#define ACI_READ_TUNERSTATION  0xa9
+#define ACI_READ_VERSION       0xf1
+#define ACI_READ_IDCODE                0xf2
+#define ACI_INIT               0xff
+#define ACI_STATUS             0xf0
+#define ACI_S_GENERAL          0x00
+#define ACI_ERROR_OP           0xdf
+
+/* ACI Mixer */
+
+/* These are the values for the right channel GET registers.
+   Add an offset of 0x01 for the left channel register.
+   (left=right+0x01) */
+
+#define ACI_GET_MASTER         0x03
+#define ACI_GET_MIC            0x05
+#define ACI_GET_LINE           0x07
+#define ACI_GET_CD             0x09
+#define ACI_GET_SYNTH          0x0b
+#define ACI_GET_PCM            0x0d
+#define ACI_GET_LINE1          0x10    /* Radio on PCM20 */
+#define ACI_GET_LINE2          0x12
+
+#define ACI_GET_EQ1            0x22    /* from Bass ... */
+#define ACI_GET_EQ2            0x24
+#define ACI_GET_EQ3            0x26
+#define ACI_GET_EQ4            0x28
+#define ACI_GET_EQ5            0x2a
+#define ACI_GET_EQ6            0x2c
+#define ACI_GET_EQ7            0x2e    /* ... to Treble */
+
+/* And these are the values for the right channel SET registers.
+   For left channel access you have to add an offset of 0x08.
+   MASTER is an exception, which needs an offset of 0x01 */
+
+#define ACI_SET_MASTER         0x00
+#define ACI_SET_MIC            0x30
+#define ACI_SET_LINE           0x31
+#define ACI_SET_CD             0x34
+#define ACI_SET_SYNTH          0x33
+#define ACI_SET_PCM            0x32
+#define ACI_SET_LINE1          0x35    /* Radio on PCM20 */
+#define ACI_SET_LINE2          0x36
+
+#define ACI_SET_EQ1            0x40    /* from Bass ... */
+#define ACI_SET_EQ2            0x41
+#define ACI_SET_EQ3            0x42
+#define ACI_SET_EQ4            0x43
+#define ACI_SET_EQ5            0x44
+#define ACI_SET_EQ6            0x45
+#define ACI_SET_EQ7            0x46    /* ... to Treble */
+
+#endif  /* _MIRO_H_ */
index 1e2e193..a208180 100644 (file)
@@ -15,6 +15,18 @@ config SND_AD1889
          To compile this as a module, choose M here: the module
          will be called snd-ad1889.
 
+config SND_ALS300
+        tristate "Avance Logic ALS300/ALS300+"
+        depends on SND
+        select SND_PCM
+        select SND_AC97_CODEC
+        select SND_OPL3_LIB
+        help
+          Say 'Y' or 'M' to include support for Avance Logic ALS300/ALS300+
+
+          To compile this driver as a module, choose M here: the module
+          will be called snd-als300
+
 config SND_ALS4000
        tristate "Avance Logic ALS4000"
        depends on SND && ISA_DMA_API
@@ -195,8 +207,9 @@ config SND_CS46XX
          will be called snd-cs46xx.
 
 config SND_CS46XX_NEW_DSP
-       bool "Cirrus Logic (Sound Fusion) New DSP support (EXPERIMENTAL)"
-       depends on SND_CS46XX && EXPERIMENTAL
+       bool "Cirrus Logic (Sound Fusion) New DSP support"
+       depends on SND_CS46XX
+        default y
        help
          Say Y here to use a new DSP image for SPDIF and dual codecs.
 
@@ -466,6 +479,19 @@ config SND_PCXHR
          To compile this driver as a module, choose M here: the module
          will be called snd-pcxhr.
 
+config SND_RIPTIDE
+       tristate "Conexant Riptide"
+       depends on SND
+       depends on FW_LOADER
+       select SND_OPL3_LIB
+       select SND_MPU401_UART
+       select SND_AC97_CODEC
+       help
+         Say 'Y' or 'M' to include support for Conexant Riptide chip.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-riptide
+
 config SND_RME32
        tristate "RME Digi32, 32/8, 32 PRO"
        depends on SND
index a6c3cd5..cba5105 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 snd-ad1889-objs := ad1889.o
+snd-als300-objs := als300.o
 snd-als4000-objs := als4000.o
 snd-atiixp-objs := atiixp.o
 snd-atiixp-modem-objs := atiixp_modem.o
@@ -27,6 +28,7 @@ snd-via82xx-modem-objs := via82xx_modem.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AD1889) += snd-ad1889.o
+obj-$(CONFIG_SND_ALS300) += snd-als300.o
 obj-$(CONFIG_SND_ALS4000) += snd-als4000.o
 obj-$(CONFIG_SND_ATIIXP) += snd-atiixp.o
 obj-$(CONFIG_SND_ATIIXP_MODEM) += snd-atiixp-modem.o
@@ -62,6 +64,7 @@ obj-$(CONFIG_SND) += \
        mixart/ \
        nm256/ \
        pcxhr/ \
+       riptide/ \
        rme9652/ \
        trident/ \
        ymfpci/ \
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
new file mode 100644 (file)
index 0000000..37b8057
--- /dev/null
@@ -0,0 +1,866 @@
+/*
+ *  als300.c - driver for Avance Logic ALS300/ALS300+ soundcards.
+ *  Copyright (C) 2005 by Ash Willis <ashwillis@programmer.net>
+ *
+ *  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
+ *
+ *  TODO
+ *  4 channel playback for ALS300+
+ *  gameport
+ *  mpu401
+ *  opl3
+ *
+ *  NOTES
+ *  The BLOCK_COUNTER registers for the ALS300(+) return a figure related to
+ *  the position in the current period, NOT the whole buffer. It is important
+ *  to know which period we are in so we can calculate the correct pointer.
+ *  This is why we always use 2 periods. We can then use a flip-flop variable
+ *  to keep track of what period we are in.
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/ac97_codec.h>
+#include <sound/opl3.h>
+
+/* snd_als300_set_irq_flag */
+#define IRQ_DISABLE            0
+#define IRQ_ENABLE             1
+
+/* I/O port layout */
+#define AC97_ACCESS            0x00
+#define AC97_READ              0x04
+#define AC97_STATUS            0x06
+#define   AC97_DATA_AVAIL              (1<<6)
+#define   AC97_BUSY                    (1<<7)
+#define ALS300_IRQ_STATUS      0x07            /* ALS300 Only */
+#define   IRQ_PLAYBACK                 (1<<3)
+#define   IRQ_CAPTURE                  (1<<2)
+#define GCR_DATA               0x08
+#define GCR_INDEX              0x0C
+#define ALS300P_DRAM_IRQ_STATUS        0x0D            /* ALS300+ Only */
+#define MPU_IRQ_STATUS         0x0E            /* ALS300 Rev. E+, ALS300+ */
+#define ALS300P_IRQ_STATUS     0x0F            /* ALS300+ Only */
+
+/* General Control Registers */
+#define PLAYBACK_START         0x80
+#define PLAYBACK_END           0x81
+#define PLAYBACK_CONTROL       0x82
+#define   TRANSFER_START               (1<<16)
+#define   FIFO_PAUSE                   (1<<17)
+#define RECORD_START           0x83
+#define RECORD_END             0x84
+#define RECORD_CONTROL         0x85
+#define DRAM_WRITE_CONTROL     0x8B
+#define   WRITE_TRANS_START            (1<<16)
+#define   DRAM_MODE_2                  (1<<17)
+#define MISC_CONTROL           0x8C
+#define   IRQ_SET_BIT                  (1<<15)
+#define   VMUTE_NORMAL                 (1<<20)
+#define   MMUTE_NORMAL                 (1<<21)
+#define MUS_VOC_VOL            0x8E
+#define PLAYBACK_BLOCK_COUNTER 0x9A
+#define RECORD_BLOCK_COUNTER   0x9B
+
+#define DEBUG_CALLS    1
+#define DEBUG_PLAY_REC 1
+
+#if DEBUG_CALLS
+#define snd_als300_dbgcalls(format, args...) printk(format, ##args)
+#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__)
+#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__)
+#else
+#define snd_als300_dbgcalls(format, args...)
+#define snd_als300_dbgcallenter()
+#define snd_als300_dbgcallleave()
+#endif
+
+#if DEBUG_PLAY_REC
+#define snd_als300_dbgplay(format, args...) printk(KERN_ERR format, ##args)
+#else
+#define snd_als300_dbgplay(format, args...)
+#endif         
+
+enum {DEVICE_ALS300, DEVICE_ALS300_PLUS};
+
+MODULE_AUTHOR("Ash Willis <ashwillis@programmer.net>");
+MODULE_DESCRIPTION("Avance Logic ALS300");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS300},{Avance Logic,ALS300+}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+struct snd_als300 {
+       unsigned long port;
+       spinlock_t reg_lock;
+       struct snd_card *card;
+       struct pci_dev *pci;
+
+       struct snd_pcm *pcm;
+       struct snd_pcm_substream *playback_substream;
+       struct snd_pcm_substream *capture_substream;
+
+       struct snd_ac97 *ac97;
+       struct snd_opl3 *opl3;
+
+       struct resource *res_port;
+
+       int irq;
+
+       int chip_type; /* ALS300 or ALS300+ */
+
+       char revision;  
+};
+
+struct snd_als300_substream_data {
+       int period_flipflop;
+       int control_register;
+       int block_counter_register;
+};
+
+static struct pci_device_id snd_als300_ids[] = {
+       { 0x4005, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300 },
+       { 0x4005, 0x0308, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300_PLUS },
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_als300_ids);
+
+static inline u32 snd_als300_gcr_read(unsigned long port, unsigned short reg)
+{
+       outb(reg, port+GCR_INDEX);
+       return inl(port+GCR_DATA);
+}
+
+static inline void snd_als300_gcr_write(unsigned long port,
+                                               unsigned short reg, u32 val)
+{
+       outb(reg, port+GCR_INDEX);
+       outl(val, port+GCR_DATA);
+}
+
+/* Enable/Disable Interrupts */
+static void snd_als300_set_irq_flag(struct snd_als300 *chip, int cmd)
+{
+       u32 tmp = snd_als300_gcr_read(chip->port, MISC_CONTROL);
+       snd_als300_dbgcallenter();
+
+       /* boolean XOR check, since old vs. new hardware have
+          directly reversed bit setting for ENABLE and DISABLE.
+          ALS300+ acts like newer versions of ALS300 */
+       if (((chip->revision > 5 || chip->chip_type == DEVICE_ALS300_PLUS) ^
+                                               (cmd == IRQ_ENABLE)) == 0)
+               tmp |= IRQ_SET_BIT;
+       else
+               tmp &= ~IRQ_SET_BIT;
+       snd_als300_gcr_write(chip->port, MISC_CONTROL, tmp);
+       snd_als300_dbgcallleave();
+}
+
+static int snd_als300_free(struct snd_als300 *chip)
+{
+       snd_als300_dbgcallenter();
+       snd_als300_set_irq_flag(chip, IRQ_DISABLE);
+       if (chip->irq >= 0)
+               free_irq(chip->irq, (void *)chip);
+       pci_release_regions(chip->pci);
+       pci_disable_device(chip->pci);
+       kfree(chip);
+       snd_als300_dbgcallleave();
+       return 0;
+}
+
+static int snd_als300_dev_free(struct snd_device *device)
+{
+       struct snd_als300 *chip = device->device_data;
+       return snd_als300_free(chip);
+}
+
+static irqreturn_t snd_als300_interrupt(int irq, void *dev_id,
+                                               struct pt_regs *regs)
+{
+       u8 status;
+       struct snd_als300 *chip = dev_id;
+       struct snd_als300_substream_data *data;
+
+       status = inb(chip->port+ALS300_IRQ_STATUS);
+       if (!status) /* shared IRQ, for different device?? Exit ASAP! */
+               return IRQ_NONE;
+
+       /* ACK everything ASAP */
+       outb(status, chip->port+ALS300_IRQ_STATUS);
+       if (status & IRQ_PLAYBACK) {
+               if (chip->pcm && chip->playback_substream) {
+                       data = chip->playback_substream->runtime->private_data;
+                       data->period_flipflop ^= 1;
+                       snd_pcm_period_elapsed(chip->playback_substream);
+                       snd_als300_dbgplay("IRQ_PLAYBACK\n");
+               }
+       }
+       if (status & IRQ_CAPTURE) {
+               if (chip->pcm && chip->capture_substream) {
+                       data = chip->capture_substream->runtime->private_data;
+                       data->period_flipflop ^= 1;
+                       snd_pcm_period_elapsed(chip->capture_substream);
+                       snd_als300_dbgplay("IRQ_CAPTURE\n");
+               }
+       }
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t snd_als300plus_interrupt(int irq, void *dev_id,
+                                               struct pt_regs *regs)
+{
+       u8 general, mpu, dram;
+       struct snd_als300 *chip = dev_id;
+       struct snd_als300_substream_data *data;
+       
+       general = inb(chip->port+ALS300P_IRQ_STATUS);
+       mpu = inb(chip->port+MPU_IRQ_STATUS);
+       dram = inb(chip->port+ALS300P_DRAM_IRQ_STATUS);
+
+       /* shared IRQ, for different device?? Exit ASAP! */
+       if ((general == 0) && ((mpu & 0x80) == 0) && ((dram & 0x01) == 0))
+               return IRQ_NONE;
+
+       if (general & IRQ_PLAYBACK) {
+               if (chip->pcm && chip->playback_substream) {
+                       outb(IRQ_PLAYBACK, chip->port+ALS300P_IRQ_STATUS);
+                       data = chip->playback_substream->runtime->private_data;
+                       data->period_flipflop ^= 1;
+                       snd_pcm_period_elapsed(chip->playback_substream);
+                       snd_als300_dbgplay("IRQ_PLAYBACK\n");
+               }
+       }
+       if (general & IRQ_CAPTURE) {
+               if (chip->pcm && chip->capture_substream) {
+                       outb(IRQ_CAPTURE, chip->port+ALS300P_IRQ_STATUS);
+                       data = chip->capture_substream->runtime->private_data;
+                       data->period_flipflop ^= 1;
+                       snd_pcm_period_elapsed(chip->capture_substream);
+                       snd_als300_dbgplay("IRQ_CAPTURE\n");
+               }
+       }
+       /* FIXME: Ack other interrupt types. Not important right now as
+        * those other devices aren't enabled. */
+       return IRQ_HANDLED;
+}
+
+static void __devexit snd_als300_remove(struct pci_dev *pci)
+{
+       snd_als300_dbgcallenter();
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+       snd_als300_dbgcallleave();
+}
+
+static unsigned short snd_als300_ac97_read(struct snd_ac97 *ac97,
+                                                       unsigned short reg)
+{
+       int i;
+       struct snd_als300 *chip = ac97->private_data;
+
+       for (i = 0; i < 1000; i++) {
+               if ((inb(chip->port+AC97_STATUS) & (AC97_BUSY)) == 0)
+                       break;
+               udelay(10);
+       }
+       outl((reg << 24) | (1 << 31), chip->port+AC97_ACCESS);
+
+       for (i = 0; i < 1000; i++) {
+               if ((inb(chip->port+AC97_STATUS) & (AC97_DATA_AVAIL)) != 0)
+                       break;
+               udelay(10);
+       }
+       return inw(chip->port+AC97_READ);
+}
+
+static void snd_als300_ac97_write(struct snd_ac97 *ac97,
+                               unsigned short reg, unsigned short val)
+{
+       int i;
+       struct snd_als300 *chip = ac97->private_data;
+
+       for (i = 0; i < 1000; i++) {
+               if ((inb(chip->port+AC97_STATUS) & (AC97_BUSY)) == 0)
+                       break;
+               udelay(10);
+       }
+       outl((reg << 24) | val, chip->port+AC97_ACCESS);
+}
+
+static int snd_als300_ac97(struct snd_als300 *chip)
+{
+       struct snd_ac97_bus *bus;
+       struct snd_ac97_template ac97;
+       int err;
+       static struct snd_ac97_bus_ops ops = {
+               .write = snd_als300_ac97_write,
+               .read = snd_als300_ac97_read,
+       };
+
+       snd_als300_dbgcallenter();
+       if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus)) < 0)
+               return err;
+
+       memset(&ac97, 0, sizeof(ac97));
+       ac97.private_data = chip;
+
+       snd_als300_dbgcallleave();
+       return snd_ac97_mixer(bus, &ac97, &chip->ac97);
+}
+
+/* hardware definition
+ *
+ * In AC97 mode, we always use 48k/16bit/stereo.
+ * Any request to change data type is ignored by
+ * the card when it is running outside of legacy
+ * mode.
+ */
+static struct snd_pcm_hardware snd_als300_playback_hw =
+{
+       .info =                 (SNDRV_PCM_INFO_MMAP |
+                               SNDRV_PCM_INFO_INTERLEAVED |
+                               SNDRV_PCM_INFO_PAUSE |
+                               SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =              SNDRV_PCM_FMTBIT_S16,
+       .rates =                SNDRV_PCM_RATE_48000,
+       .rate_min =             48000,
+       .rate_max =             48000,
+       .channels_min =         2,
+       .channels_max =         2,
+       .buffer_bytes_max =     64 * 1024,
+       .period_bytes_min =     64,
+       .period_bytes_max =     32 * 1024,
+       .periods_min =          2,
+       .periods_max =          2,
+};
+
+static struct snd_pcm_hardware snd_als300_capture_hw =
+{
+       .info =                 (SNDRV_PCM_INFO_MMAP |
+                               SNDRV_PCM_INFO_INTERLEAVED |
+                               SNDRV_PCM_INFO_PAUSE |
+                               SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =              SNDRV_PCM_FMTBIT_S16,
+       .rates =                SNDRV_PCM_RATE_48000,
+       .rate_min =             48000,
+       .rate_max =             48000,
+       .channels_min =         2,
+       .channels_max =         2,
+       .buffer_bytes_max =     64 * 1024,
+       .period_bytes_min =     64,
+       .period_bytes_max =     32 * 1024,
+       .periods_min =          2,
+       .periods_max =          2,
+};
+
+static int snd_als300_playback_open(struct snd_pcm_substream *substream)
+{
+       struct snd_als300 *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_als300_substream_data *data = kzalloc(sizeof(*data),
+                                                               GFP_KERNEL);
+
+       snd_als300_dbgcallenter();
+       chip->playback_substream = substream;
+       runtime->hw = snd_als300_playback_hw;
+       runtime->private_data = data;
+       data->control_register = PLAYBACK_CONTROL;
+       data->block_counter_register = PLAYBACK_BLOCK_COUNTER;
+       snd_als300_dbgcallleave();
+       return 0;
+}
+
+static int snd_als300_playback_close(struct snd_pcm_substream *substream)
+{
+       struct snd_als300 *chip = snd_pcm_substream_chip(substream);
+       struct snd_als300_substream_data *data;
+
+       data = substream->runtime->private_data;
+       snd_als300_dbgcallenter();
+       kfree(data);
+       chip->playback_substream = NULL;
+       snd_pcm_lib_free_pages(substream);
+       snd_als300_dbgcallleave();
+       return 0;
+}
+
+static int snd_als300_capture_open(struct snd_pcm_substream *substream)
+{
+       struct snd_als300 *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_als300_substream_data *data = kzalloc(sizeof(*data),
+                                                               GFP_KERNEL);
+
+       snd_als300_dbgcallenter();
+       chip->capture_substream = substream;
+       runtime->hw = snd_als300_capture_hw;
+       runtime->private_data = data;
+       data->control_register = RECORD_CONTROL;
+       data->block_counter_register = RECORD_BLOCK_COUNTER;
+       snd_als300_dbgcallleave();
+       return 0;
+}
+
+static int snd_als300_capture_close(struct snd_pcm_substream *substream)
+{
+       struct snd_als300 *chip = snd_pcm_substream_chip(substream);
+       struct snd_als300_substream_data *data;
+
+       data = substream->runtime->private_data;
+       snd_als300_dbgcallenter();
+       kfree(data);
+       chip->capture_substream = NULL;
+       snd_pcm_lib_free_pages(substream);
+       snd_als300_dbgcallleave();
+       return 0;
+}
+
+static int snd_als300_pcm_hw_params(struct snd_pcm_substream *substream,
+                                       snd_pcm_hw_params_t * hw_params)
+{
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+static int snd_als300_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_als300_playback_prepare(struct snd_pcm_substream *substream)
+{
+       u32 tmp;
+       struct snd_als300 *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned short period_bytes = snd_pcm_lib_period_bytes(substream);
+       unsigned short buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+       
+       snd_als300_dbgcallenter();
+       spin_lock_irq(&chip->reg_lock);
+       tmp = snd_als300_gcr_read(chip->port, PLAYBACK_CONTROL);
+       tmp &= ~TRANSFER_START;
+
+       snd_als300_dbgplay("Period bytes: %d Buffer bytes %d\n",
+                                               period_bytes, buffer_bytes);
+       
+       /* set block size */
+       tmp &= 0xffff0000;
+       tmp |= period_bytes - 1;
+       snd_als300_gcr_write(chip->port, PLAYBACK_CONTROL, tmp);
+
+       /* set dma area */
+       snd_als300_gcr_write(chip->port, PLAYBACK_START,
+                                       runtime->dma_addr);
+       snd_als300_gcr_write(chip->port, PLAYBACK_END,
+                                       runtime->dma_addr + buffer_bytes - 1);
+       spin_unlock_irq(&chip->reg_lock);
+       snd_als300_dbgcallleave();
+       return 0;
+}
+
+static int snd_als300_capture_prepare(struct snd_pcm_substream *substream)
+{
+       u32 tmp;
+       struct snd_als300 *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned short period_bytes = snd_pcm_lib_period_bytes(substream);
+       unsigned short buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+
+       snd_als300_dbgcallenter();
+       spin_lock_irq(&chip->reg_lock);
+       tmp = snd_als300_gcr_read(chip->port, RECORD_CONTROL);
+       tmp &= ~TRANSFER_START;
+
+       snd_als300_dbgplay("Period bytes: %d Buffer bytes %d\n", period_bytes,
+                                                       buffer_bytes);
+
+       /* set block size */
+       tmp &= 0xffff0000;
+       tmp |= period_bytes - 1;
+
+       /* set dma area */
+       snd_als300_gcr_write(chip->port, RECORD_CONTROL, tmp);
+       snd_als300_gcr_write(chip->port, RECORD_START,
+                                       runtime->dma_addr);
+       snd_als300_gcr_write(chip->port, RECORD_END,
+                                       runtime->dma_addr + buffer_bytes - 1);
+       spin_unlock_irq(&chip->reg_lock);
+       snd_als300_dbgcallleave();
+       return 0;
+}
+
+static int snd_als300_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_als300 *chip = snd_pcm_substream_chip(substream);
+       u32 tmp;
+       struct snd_als300_substream_data *data;
+       unsigned short reg;
+       int ret = 0;
+
+       data = substream->runtime->private_data;
+       reg = data->control_register;
+
+       snd_als300_dbgcallenter();
+       spin_lock(&chip->reg_lock);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               tmp = snd_als300_gcr_read(chip->port, reg);
+               data->period_flipflop = 1;
+               snd_als300_gcr_write(chip->port, reg, tmp | TRANSFER_START);
+               snd_als300_dbgplay("TRIGGER START\n");
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               tmp = snd_als300_gcr_read(chip->port, reg);
+               snd_als300_gcr_write(chip->port, reg, tmp & ~TRANSFER_START);
+               snd_als300_dbgplay("TRIGGER STOP\n");
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               tmp = snd_als300_gcr_read(chip->port, reg);
+               snd_als300_gcr_write(chip->port, reg, tmp | FIFO_PAUSE);
+               snd_als300_dbgplay("TRIGGER PAUSE\n");
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               tmp = snd_als300_gcr_read(chip->port, reg);
+               snd_als300_gcr_write(chip->port, reg, tmp & ~FIFO_PAUSE);
+               snd_als300_dbgplay("TRIGGER RELEASE\n");
+               break;
+       default:
+               snd_als300_dbgplay("TRIGGER INVALID\n");
+               ret = -EINVAL;
+       }
+       spin_unlock(&chip->reg_lock);
+       snd_als300_dbgcallleave();
+       return ret;
+}
+
+static snd_pcm_uframes_t snd_als300_pointer(struct snd_pcm_substream *substream)
+{
+       u16 current_ptr;
+       struct snd_als300 *chip = snd_pcm_substream_chip(substream);
+       struct snd_als300_substream_data *data;
+       unsigned short period_bytes;
+
+       data = substream->runtime->private_data;
+       period_bytes = snd_pcm_lib_period_bytes(substream);
+       
+       snd_als300_dbgcallenter();
+       spin_lock(&chip->reg_lock);
+       current_ptr = (u16) snd_als300_gcr_read(chip->port,
+                                       data->block_counter_register) + 4;
+       spin_unlock(&chip->reg_lock);
+       if (current_ptr > period_bytes)
+               current_ptr = 0;
+       else
+               current_ptr = period_bytes - current_ptr;
+
+       if (data->period_flipflop == 0)
+               current_ptr += period_bytes;
+       snd_als300_dbgplay("Pointer (bytes): %d\n", current_ptr);
+       snd_als300_dbgcallleave();
+       return bytes_to_frames(substream->runtime, current_ptr);
+}
+
+static struct snd_pcm_ops snd_als300_playback_ops = {
+       .open =         snd_als300_playback_open,
+       .close =        snd_als300_playback_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_als300_pcm_hw_params,
+       .hw_free =      snd_als300_pcm_hw_free,
+       .prepare =      snd_als300_playback_prepare,
+       .trigger =      snd_als300_trigger,
+       .pointer =      snd_als300_pointer,
+};
+
+static struct snd_pcm_ops snd_als300_capture_ops = {
+       .open =         snd_als300_capture_open,
+       .close =        snd_als300_capture_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_als300_pcm_hw_params,
+       .hw_free =      snd_als300_pcm_hw_free,
+       .prepare =      snd_als300_capture_prepare,
+       .trigger =      snd_als300_trigger,
+       .pointer =      snd_als300_pointer,
+};
+
+static int __devinit snd_als300_new_pcm(struct snd_als300 *chip)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       snd_als300_dbgcallenter();
+       err = snd_pcm_new(chip->card, "ALS300", 0, 1, 1, &pcm);
+       if (err < 0)
+               return err;
+       pcm->private_data = chip;
+       strcpy(pcm->name, "ALS300");
+       chip->pcm = pcm;
+
+       /* set operators */
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                               &snd_als300_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                               &snd_als300_capture_ops);
+
+       /* pre-allocation of buffers */
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+       snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+       snd_als300_dbgcallleave();
+       return 0;
+}
+
+static void snd_als300_init(struct snd_als300 *chip)
+{
+       unsigned long flags;
+       u32 tmp;
+       
+       snd_als300_dbgcallenter();
+       spin_lock_irqsave(&chip->reg_lock, flags);
+       chip->revision = (snd_als300_gcr_read(chip->port, MISC_CONTROL) >> 16)
+                                                               & 0x0000000F;
+       /* Setup DRAM */
+       tmp = snd_als300_gcr_read(chip->port, DRAM_WRITE_CONTROL);
+       snd_als300_gcr_write(chip->port, DRAM_WRITE_CONTROL,
+                                               (tmp | DRAM_MODE_2)
+                                               & ~WRITE_TRANS_START);
+
+       /* Enable IRQ output */
+       snd_als300_set_irq_flag(chip, IRQ_ENABLE);
+
+       /* Unmute hardware devices so their outputs get routed to
+        * the onboard mixer */
+       tmp = snd_als300_gcr_read(chip->port, MISC_CONTROL);
+       snd_als300_gcr_write(chip->port, MISC_CONTROL,
+                       tmp | VMUTE_NORMAL | MMUTE_NORMAL);
+
+       /* Reset volumes */
+       snd_als300_gcr_write(chip->port, MUS_VOC_VOL, 0);
+
+       /* Make sure playback transfer is stopped */
+       tmp = snd_als300_gcr_read(chip->port, PLAYBACK_CONTROL);
+       snd_als300_gcr_write(chip->port, PLAYBACK_CONTROL,
+                       tmp & ~TRANSFER_START);
+       spin_unlock_irqrestore(&chip->reg_lock, flags);
+       snd_als300_dbgcallleave();
+}
+
+static int __devinit snd_als300_create(snd_card_t *card,
+                                      struct pci_dev *pci, int chip_type,
+                                      struct snd_als300 **rchip)
+{
+       struct snd_als300 *chip;
+       void *irq_handler;
+       int err;
+
+       static snd_device_ops_t ops = {
+               .dev_free = snd_als300_dev_free,
+       };
+       *rchip = NULL;
+
+       snd_als300_dbgcallenter();
+       if ((err = pci_enable_device(pci)) < 0)
+               return err;
+
+       if (pci_set_dma_mask(pci, 0x0fffffff) < 0 ||
+               pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) {
+               printk(KERN_ERR "error setting 28bit DMA mask\n");
+               pci_disable_device(pci);
+               return -ENXIO;
+       }
+       pci_set_master(pci);
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (chip == NULL) {
+               pci_disable_device(pci);
+               return -ENOMEM;
+       }
+
+       chip->card = card;
+       chip->pci = pci;
+       chip->irq = -1;
+       chip->chip_type = chip_type;
+       spin_lock_init(&chip->reg_lock);
+
+       if ((err = pci_request_regions(pci, "ALS300")) < 0) {
+               kfree(chip);
+               pci_disable_device(pci);
+               return err;
+       }
+       chip->port = pci_resource_start(pci, 0);
+
+       if (chip->chip_type == DEVICE_ALS300_PLUS)
+               irq_handler = snd_als300plus_interrupt;
+       else
+               irq_handler = snd_als300_interrupt;
+
+       if (request_irq(pci->irq, irq_handler, SA_INTERRUPT|SA_SHIRQ,
+                                       card->shortname, (void *)chip)) {
+               snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+               snd_als300_free(chip);
+               return -EBUSY;
+       }
+       chip->irq = pci->irq;
+
+
+       snd_als300_init(chip);
+
+       if (snd_als300_ac97(chip) < 0) {
+               snd_printk(KERN_WARNING "Could not create ac97\n");
+               snd_als300_free(chip);
+               return err;
+       }
+
+       if ((err = snd_als300_new_pcm(chip)) < 0) {
+               snd_printk(KERN_WARNING "Could not create PCM\n");
+               snd_als300_free(chip);
+               return err;
+       }
+
+       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
+                                               chip, &ops)) < 0) {
+               snd_als300_free(chip);
+               return err;
+       }
+
+       snd_card_set_dev(card, &pci->dev);
+
+       *rchip = chip;
+       snd_als300_dbgcallleave();
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_als300_suspend(struct pci_dev *pci, pm_message_t state)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct snd_als300 *chip = card->private_data;
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+       snd_pcm_suspend_all(chip->pcm);
+       snd_ac97_suspend(chip->ac97);
+
+       pci_set_power_state(pci, PCI_D3hot);
+       pci_disable_device(pci);
+       pci_save_state(pci);
+       return 0;
+}
+
+static int snd_als300_resume(struct pci_dev *pci)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct snd_als300 *chip = card->private_data;
+
+       pci_restore_state(pci);
+       pci_enable_device(pci);
+       pci_set_power_state(pci, PCI_D0);
+       pci_set_master(pci);
+
+       snd_als300_init(chip);
+       snd_ac97_resume(chip->ac97);
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+       return 0;
+}
+#endif
+
+static int __devinit snd_als300_probe(struct pci_dev *pci,
+                             const struct pci_device_id *pci_id)
+{
+       static int dev;
+       struct snd_card *card;
+       struct snd_als300 *chip;
+       int err, chip_type;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+
+       if (card == NULL)
+               return -ENOMEM;
+
+       chip_type = pci_id->driver_data;
+
+       if ((err = snd_als300_create(card, pci, chip_type, &chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       card->private_data = chip;
+
+       strcpy(card->driver, "ALS300");
+       if (chip->chip_type == DEVICE_ALS300_PLUS)
+               /* don't know much about ALS300+ yet
+                * print revision number for now */
+               sprintf(card->shortname, "ALS300+ (Rev. %d)", chip->revision);
+       else
+               sprintf(card->shortname, "ALS300 (Rev. %c)", 'A' +
+                                                       chip->revision - 1);
+       sprintf(card->longname, "%s at 0x%lx irq %i",
+                               card->shortname, chip->port, chip->irq);
+
+       if ((err = snd_card_register(card)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       pci_set_drvdata(pci, card);
+       dev++;
+       return 0;
+}
+
+static struct pci_driver driver = {
+       .name = "ALS300",
+       .id_table = snd_als300_ids,
+       .probe = snd_als300_probe,
+       .remove = __devexit_p(snd_als300_remove),
+#ifdef CONFIG_PM
+       .suspend = snd_als300_suspend,
+       .resume = snd_als300_resume,
+#endif
+};
+
+static int __init alsa_card_als300_init(void)
+{
+       return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_als300_exit(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_als300_init)
+module_exit(alsa_card_als300_exit)
index 4f65ec5..ac4e73f 100644 (file)
@@ -1046,7 +1046,7 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol,
                snd_cs4281_pokeBA0(chip, regL, volL);
                change = 1;
        }
-       if (ucontrol->value.integer.value[0] != volL) {
+       if (ucontrol->value.integer.value[1] != volR) {
                volR = CS_VOL_MASK - (ucontrol->value.integer.value[1] & CS_VOL_MASK);
                snd_cs4281_pokeBA0(chip, regR, volR);
                change = 1;
@@ -1416,7 +1416,7 @@ static int __devinit snd_cs4281_create(struct snd_card *card,
 static int snd_cs4281_chip_init(struct cs4281 *chip)
 {
        unsigned int tmp;
-       int timeout;
+       unsigned long end_time;
        int retry_count = 2;
 
        /* Having EPPMC.FPDN=1 prevent proper chip initialisation */
@@ -1496,7 +1496,7 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
        /*
         * Wait for the DLL ready signal from the clock logic.
         */
-       timeout = 100;
+       end_time = jiffies + HZ;
        do {
                /*
                 *  Read the AC97 status register to see if we've seen a CODEC
@@ -1504,8 +1504,8 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
                 */
                if (snd_cs4281_peekBA0(chip, BA0_CLKCR1) & BA0_CLKCR1_DLLRDY)
                        goto __ok0;
-               msleep(1);
-       } while (timeout-- > 0);
+               schedule_timeout_uninterruptible(1);
+       } while (time_after_eq(end_time, jiffies));
 
        snd_printk(KERN_ERR "DLLRDY not seen\n");
        return -EIO;
@@ -1522,7 +1522,7 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
        /*
         * Wait for the codec ready signal from the AC97 codec.
         */
-       timeout = 100;
+       end_time = jiffies + HZ;
        do {
                /*
                 *  Read the AC97 status register to see if we've seen a CODEC
@@ -1530,20 +1530,20 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
                 */
                if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_CRDY)
                        goto __ok1;
-               msleep(1);
-       } while (timeout-- > 0);
+               schedule_timeout_uninterruptible(1);
+       } while (time_after_eq(end_time, jiffies));
 
        snd_printk(KERN_ERR "never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS));
        return -EIO;
 
       __ok1:
        if (chip->dual_codec) {
-               timeout = 100;
+               end_time = jiffies + HZ;
                do {
                        if (snd_cs4281_peekBA0(chip, BA0_ACSTS2) & BA0_ACSTS_CRDY)
                                goto __codec2_ok;
-                       msleep(1);
-               } while (timeout-- > 0);
+                       schedule_timeout_uninterruptible(1);
+               } while (time_after_eq(end_time, jiffies));
                snd_printk(KERN_INFO "secondary codec doesn't respond. disable it...\n");
                chip->dual_codec = 0;
        __codec2_ok: ;
@@ -1561,7 +1561,7 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
         *  the codec is pumping ADC data across the AC-link.
         */
 
-       timeout = 100;
+       end_time = jiffies + HZ;
        do {
                /*
                 *  Read the input slot valid register and see if input slots 3
@@ -1569,8 +1569,8 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
                 */
                 if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4)))
                         goto __ok2;
-               msleep(1);
-       } while (timeout-- > 0);
+               schedule_timeout_uninterruptible(1);
+       } while (time_after_eq(end_time, jiffies));
 
        if (--retry_count > 0)
                goto __retry;
index b42dff7..5bee3b5 100644 (file)
@@ -295,7 +295,7 @@ static int init_unsol_queue(struct hda_bus *bus)
                snd_printk(KERN_ERR "hda_codec: can't allocate unsolicited queue\n");
                return -ENOMEM;
        }
-       unsol->workq = create_workqueue("hda_codec");
+       unsol->workq = create_singlethread_workqueue("hda_codec");
        if (! unsol->workq) {
                snd_printk(KERN_ERR "hda_codec: can't create workqueue\n");
                kfree(unsol);
index c096606..0ad60ae 100644 (file)
@@ -81,6 +81,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
                         "{Intel, ESB2},"
                         "{Intel, ICH8},"
                         "{ATI, SB450},"
+                        "{ATI, SB600},"
                         "{VIA, VT8251},"
                         "{VIA, VT8237A},"
                         "{SiS, SIS966},"
@@ -1619,6 +1620,7 @@ static struct pci_device_id azx_ids[] = {
        { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */
        { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
        { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
+       { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
        { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
        { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
        { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
index 32401bd..2bfe37e 100644 (file)
@@ -44,6 +44,7 @@ struct ad198x_spec {
                                         * dig_out_nid and hp_nid are optional
                                         */
        unsigned int cur_eapd;
+       unsigned int need_dac_fix;
 
        /* capture */
        unsigned int num_adc_nids;
@@ -836,10 +837,14 @@ static int patch_ad1986a(struct hda_codec *codec)
        case AD1986A_3STACK:
                spec->num_mixers = 2;
                spec->mixers[1] = ad1986a_3st_mixers;
-               spec->num_init_verbs = 2;
+               spec->num_init_verbs = 3;
                spec->init_verbs[1] = ad1986a_3st_init_verbs;
+               spec->init_verbs[2] = ad1986a_ch2_init;
                spec->channel_mode = ad1986a_modes;
                spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
+               spec->need_dac_fix = 1;
+               spec->multiout.max_channels = 2;
+               spec->multiout.num_dacs = 1;
                break;
        case AD1986A_LAPTOP:
                spec->mixers[0] = ad1986a_laptop_mixers;
@@ -1555,6 +1560,8 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct ad198x_spec *spec = codec->spec;
+       if (spec->need_dac_fix)
+               spec->multiout.num_dacs = spec->multiout.max_channels / 2;
        return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
                                   spec->num_channel_mode, &spec->multiout.max_channels);
 }
index 4c6c9ec..66bbdb6 100644 (file)
@@ -52,6 +52,7 @@ enum {
        ALC880_CLEVO,
        ALC880_TCL_S700,
        ALC880_LG,
+       ALC880_LG_LW,
 #ifdef CONFIG_SND_DEBUG
        ALC880_TEST,
 #endif
@@ -131,6 +132,7 @@ struct alc_spec {
        hda_nid_t dig_in_nid;           /* digital-in NID; optional */
 
        /* capture source */
+       unsigned int num_mux_defs;
        const struct hda_input_mux *input_mux;
        unsigned int cur_mux[3];
 
@@ -172,6 +174,7 @@ struct alc_config_preset {
        hda_nid_t dig_in_nid;
        unsigned int num_channel_mode;
        const struct hda_channel_mode *channel_mode;
+       unsigned int num_mux_defs;
        const struct hda_input_mux *input_mux;
        void (*unsol_event)(struct hda_codec *, unsigned int);
        void (*init_hook)(struct hda_codec *);
@@ -185,7 +188,10 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct alc_spec *spec = codec->spec;
-       return snd_hda_input_mux_info(spec->input_mux, uinfo);
+       unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id);
+       if (mux_idx >= spec->num_mux_defs)
+               mux_idx = 0;
+       return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
 }
 
 static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -203,7 +209,8 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct alc_spec *spec = codec->spec;
        unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+       unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+       return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol,
                                     spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
 }
 
@@ -245,7 +252,8 @@ static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
  * states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these
  * are requested.  Therefore order this list so that this behaviour will not
  * cause problems when mixer clients move through the enum sequentially.
- * NIDs 0x0f and 0x10 have been observed to have this behaviour.
+ * NIDs 0x0f and 0x10 have been observed to have this behaviour as of
+ * March 2006.
  */
 static char *alc_pin_mode_names[] = {
        "Mic 50pc bias", "Mic 80pc bias",
@@ -255,19 +263,27 @@ static unsigned char alc_pin_mode_values[] = {
        PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP,
 };
 /* The control can present all 5 options, or it can limit the options based
- * in the pin being assumed to be exclusively an input or an output pin.
+ * in the pin being assumed to be exclusively an input or an output pin.  In
+ * addition, "input" pins may or may not process the mic bias option
+ * depending on actual widget capability (NIDs 0x0f and 0x10 don't seem to
+ * accept requests for bias as of chip versions up to March 2006) and/or
+ * wiring in the computer.
  */
-#define ALC_PIN_DIR_IN    0x00
-#define ALC_PIN_DIR_OUT   0x01
-#define ALC_PIN_DIR_INOUT 0x02
+#define ALC_PIN_DIR_IN              0x00
+#define ALC_PIN_DIR_OUT             0x01
+#define ALC_PIN_DIR_INOUT           0x02
+#define ALC_PIN_DIR_IN_NOMICBIAS    0x03
+#define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04
 
-/* Info about the pin modes supported by the three different pin directions. 
+/* Info about the pin modes supported by the different pin direction modes. 
  * For each direction the minimum and maximum values are given.
  */
-static signed char alc_pin_mode_dir_info[3][2] = {
+static signed char alc_pin_mode_dir_info[5][2] = {
        { 0, 2 },    /* ALC_PIN_DIR_IN */
        { 3, 4 },    /* ALC_PIN_DIR_OUT */
        { 0, 4 },    /* ALC_PIN_DIR_INOUT */
+       { 2, 2 },    /* ALC_PIN_DIR_IN_NOMICBIAS */
+       { 2, 4 },    /* ALC_PIN_DIR_INOUT_NOMICBIAS */
 };
 #define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0])
 #define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1])
@@ -329,9 +345,10 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
                 * input modes.
                 *
                 * Dynamically switching the input/output buffers probably
-                * reduces noise slightly, particularly on input.  However,
-                * havingboth input and output buffers enabled
-                * simultaneously doesn't seem to be problematic.
+                * reduces noise slightly (particularly on input) so we'll
+                * do it.  However, having both input and output buffers
+                * enabled simultaneously doesn't seem to be problematic if
+                * this turns out to be necessary in the future.
                 */
                if (val <= 2) {
                        snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
@@ -483,6 +500,9 @@ static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *
        spec->multiout.dig_out_nid = preset->dig_out_nid;
        spec->multiout.hp_nid = preset->hp_nid;
        
+       spec->num_mux_defs = preset->num_mux_defs;
+       if (! spec->num_mux_defs)
+               spec->num_mux_defs = 1;
        spec->input_mux = preset->input_mux;
 
        spec->num_adc_nids = preset->num_adc_nids;
@@ -1426,6 +1446,82 @@ static void alc880_lg_unsol_event(struct hda_codec *codec, unsigned int res)
                alc880_lg_automute(codec);
 }
 
+/*
+ * LG LW20
+ *
+ * Pin assignment:
+ *   Speaker-out: 0x14
+ *   Mic-In: 0x18
+ *   Built-in Mic-In: 0x19 (?)
+ *   HP-Out: 0x1b
+ *   SPDIF-Out: 0x1e
+ */
+
+/* seems analog CD is not working */
+static struct hda_input_mux alc880_lg_lw_capture_source = {
+       .num_items = 2,
+       .items = {
+               { "Mic", 0x0 },
+               { "Internal Mic", 0x1 },
+       },
+};
+
+static struct snd_kcontrol_new alc880_lg_lw_mixer[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+       { } /* end */
+};
+
+static struct hda_verb alc880_lg_lw_init_verbs[] = {
+       /* set capture source 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)},
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)},
+       /* speaker-out */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* HP-out */
+       {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* mic-in to input */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* built-in mic */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* jack sense */
+       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1},
+       { }
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc880_lg_lw_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+
+       present = snd_hda_codec_read(codec, 0x1b, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+                                0x80, present ? 0x80 : 0);
+       snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+                                0x80, present ? 0x80 : 0);
+}
+
+static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       /* Looks like the unsol event is incompatible with the standard
+        * definition.  4bit tag is placed at 28 bit!
+        */
+       if ((res >> 28) == 0x01)
+               alc880_lg_lw_automute(codec);
+}
+
 /*
  * Common callbacks
  */
@@ -2078,6 +2174,9 @@ static struct hda_board_config alc880_cfg_tbl[] = {
        { .modelname = "lg", .config = ALC880_LG },
        { .pci_subvendor = 0x1854, .pci_subdevice = 0x003b, .config = ALC880_LG },
 
+       { .modelname = "lg-lw", .config = ALC880_LG_LW },
+       { .pci_subvendor = 0x1854, .pci_subdevice = 0x0018, .config = ALC880_LG_LW },
+
 #ifdef CONFIG_SND_DEBUG
        { .modelname = "test", .config = ALC880_TEST },
 #endif
@@ -2268,6 +2367,19 @@ static struct alc_config_preset alc880_presets[] = {
                .unsol_event = alc880_lg_unsol_event,
                .init_hook = alc880_lg_automute,
        },
+       [ALC880_LG_LW] = {
+               .mixers = { alc880_lg_lw_mixer },
+               .init_verbs = { alc880_volume_init_verbs,
+                               alc880_lg_lw_init_verbs },
+               .num_dacs = 1, 
+               .dac_nids = alc880_dac_nids,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
+               .channel_mode = alc880_2_jack_modes,
+               .input_mux = &alc880_lg_lw_capture_source,
+               .unsol_event = alc880_lg_lw_unsol_event,
+               .init_hook = alc880_lg_lw_automute,
+       },
 #ifdef CONFIG_SND_DEBUG
        [ALC880_TEST] = {
                .mixers = { alc880_test_mixer },
@@ -2593,6 +2705,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
 
        spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
 
+       spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
 
        return 1;
@@ -2722,30 +2835,56 @@ static struct hda_input_mux alc260_capture_source = {
 };
 
 /* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack,
- * headphone jack and the internal CD lines.
+ * headphone jack and the internal CD lines since these are the only pins at
+ * which audio can appear.  For flexibility, also allow the option of
+ * recording the mixer output on the second ADC (ADC0 doesn't have a
+ * connection to the mixer output).
  */
-static struct hda_input_mux alc260_fujitsu_capture_source = {
-       .num_items = 3,
-       .items = {
-               { "Mic/Line", 0x0 },
-               { "CD", 0x4 },
-               { "Headphone", 0x2 },
+static struct hda_input_mux alc260_fujitsu_capture_sources[2] = {
+       {
+               .num_items = 3,
+               .items = {
+                       { "Mic/Line", 0x0 },
+                       { "CD", 0x4 },
+                       { "Headphone", 0x2 },
+               },
        },
+       {
+               .num_items = 4,
+               .items = {
+                       { "Mic/Line", 0x0 },
+                       { "CD", 0x4 },
+                       { "Headphone", 0x2 },
+                       { "Mixer", 0x5 },
+               },
+       },
+
 };
 
-/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configutation to
- * the Fujitsu S702x, but jacks are marked differently. We won't allow
- * retasking the Headphone jack, so it won't be available here.
+/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to
+ * the Fujitsu S702x, but jacks are marked differently.
  */
-static struct hda_input_mux alc260_acer_capture_source = {
-       .num_items = 3,
-       .items = {
-               { "Mic", 0x0 },
-               { "Line", 0x2 },
-               { "CD", 0x4 },
+static struct hda_input_mux alc260_acer_capture_sources[2] = {
+       {
+               .num_items = 4,
+               .items = {
+                       { "Mic", 0x0 },
+                       { "Line", 0x2 },
+                       { "CD", 0x4 },
+                       { "Headphone", 0x5 },
+               },
+       },
+       {
+               .num_items = 5,
+               .items = {
+                       { "Mic", 0x0 },
+                       { "Line", 0x2 },
+                       { "CD", 0x4 },
+                       { "Headphone", 0x6 },
+                       { "Mixer", 0x5 },
+               },
        },
 };
-
 /*
  * This is just place-holder, so there's something for alc_build_pcms to look
  * at when it calculates the maximum number of channels. ALC260 has no mixer
@@ -2806,6 +2945,9 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
        { } /* end */
 };
 
+/* Fujitsu S702x series laptops.  ALC260 pin usage: Mic/Line jack = 0x12, 
+ * HP jack = 0x14, CD audio =  0x16, internal speaker = 0x10.
+ */
 static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
        HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
@@ -2822,9 +2964,28 @@ static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
        { } /* end */
 };
 
+/* Mixer for Acer TravelMate(/Extensa/Aspire) notebooks.  Note that current
+ * versions of the ALC260 don't act on requests to enable mic bias from NID
+ * 0x0f (used to drive the headphone jack in these laptops).  The ALC260
+ * datasheet doesn't mention this restriction.  At this stage it's not clear
+ * whether this behaviour is intentional or is a hardware bug in chip
+ * revisions available in early 2006.  Therefore for now allow the
+ * "Headphone Jack Mode" control to span all choices, but if it turns out
+ * that the lack of mic bias for this NID is intentional we could change the
+ * mode from ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
+ *
+ * In addition, Acer TravelMate(/Extensa/Aspire) notebooks in early 2006
+ * don't appear to make the mic bias available from the "line" jack, even
+ * though the NID used for this jack (0x14) can supply it.  The theory is
+ * that perhaps Acer have included blocking capacitors between the ALC260
+ * and the output jack.  If this turns out to be the case for all such
+ * models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT
+ * to ALC_PIN_DIR_INOUT_NOMICBIAS.
+ */
 static struct snd_kcontrol_new alc260_acer_mixer[] = {
        HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
+       ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
        HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
        HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
@@ -3038,7 +3199,8 @@ static struct hda_verb alc260_hp_3013_init_verbs[] = {
 };
 
 /* Initialisation sequence for ALC260 as configured in Fujitsu S702x
- * laptops.
+ * laptops.  ALC260 pin usage: Mic/Line jack = 0x12, HP jack = 0x14, CD
+ * audio = 0x16, internal speaker = 0x10.
  */
 static struct hda_verb alc260_fujitsu_init_verbs[] = {
        /* Disable all GPIOs */
@@ -3185,10 +3347,10 @@ static struct hda_verb alc260_acer_init_verbs[] = {
        {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
 
        /* Do similar with the second ADC: mute capture input amp and
-        * set ADC connection to line (on line1 pin)
+        * set ADC connection to mic to match ALSA's default state.
         */
        {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x05, AC_VERB_SET_CONNECT_SEL, 0x02},
+       {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
 
        /* Mute all inputs to mixer widget (even unconnected ones) */
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
@@ -3213,26 +3375,35 @@ static hda_nid_t alc260_test_dac_nids[1] = {
 static hda_nid_t alc260_test_adc_nids[2] = {
        0x04, 0x05,
 };
-/* This is a bit messy since the two input muxes in the ALC260 have slight
- * variations in their signal assignments.  The ideal way to deal with this
- * is to extend alc_spec.input_mux to allow a different input MUX for each
- * ADC.  For the purposes of the test model it's sufficient to just list
- * both options for affected signal indices.  The separate input mux
- * functionality only needs to be considered if a model comes along which
- * actually uses signals 0x5, 0x6 and 0x7 for something which makes sense to
- * record.
+/* For testing the ALC260, each input MUX needs its own definition since
+ * the signal assignments are different.  This assumes that the first ADC 
+ * is NID 0x04.
  */
-static struct hda_input_mux alc260_test_capture_source = {
-       .num_items = 8,
-       .items = {
-               { "MIC1 pin", 0x0 },
-               { "MIC2 pin", 0x1 },
-               { "LINE1 pin", 0x2 },
-               { "LINE2 pin", 0x3 },
-               { "CD pin", 0x4 },
-               { "LINE-OUT pin (cap1), Mixer (cap2)", 0x5 },
-               { "HP-OUT pin (cap1), LINE-OUT pin (cap2)", 0x6 },
-               { "HP-OUT pin (cap2 only)", 0x7 },
+static struct hda_input_mux alc260_test_capture_sources[2] = {
+       {
+               .num_items = 7,
+               .items = {
+                       { "MIC1 pin", 0x0 },
+                       { "MIC2 pin", 0x1 },
+                       { "LINE1 pin", 0x2 },
+                       { "LINE2 pin", 0x3 },
+                       { "CD pin", 0x4 },
+                       { "LINE-OUT pin", 0x5 },
+                       { "HP-OUT pin", 0x6 },
+               },
+        },
+       {
+               .num_items = 8,
+               .items = {
+                       { "MIC1 pin", 0x0 },
+                       { "MIC2 pin", 0x1 },
+                       { "LINE1 pin", 0x2 },
+                       { "LINE2 pin", 0x3 },
+                       { "CD pin", 0x4 },
+                       { "Mixer", 0x5 },
+                       { "LINE-OUT pin", 0x6 },
+                       { "HP-OUT pin", 0x7 },
+               },
         },
 };
 static struct snd_kcontrol_new alc260_test_mixer[] = {
@@ -3244,7 +3415,17 @@ static struct snd_kcontrol_new alc260_test_mixer[] = {
        HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT),
 
-       /* Modes for retasking pin widgets */
+       /* Modes for retasking pin widgets
+        * Note: the ALC260 doesn't seem to act on requests to enable mic
+         * bias from NIDs 0x0f and 0x10.  The ALC260 datasheet doesn't
+         * mention this restriction.  At this stage it's not clear whether
+         * this behaviour is intentional or is a hardware bug in chip
+         * revisions available at least up until early 2006.  Therefore for
+         * now allow the "HP-OUT" and "LINE-OUT" Mode controls to span all
+         * choices, but if it turns out that the lack of mic bias for these
+         * NIDs is intentional we could change their modes from
+         * ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
+        */
        ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT),
        ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT),
        ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT),
@@ -3606,6 +3787,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
 
        spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
 
+       spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
 
        /* check whether NID 0x04 is valid */
@@ -3711,7 +3893,8 @@ static struct alc_config_preset alc260_presets[] = {
                .adc_nids = alc260_dual_adc_nids,
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
-               .input_mux = &alc260_fujitsu_capture_source,
+               .num_mux_defs = ARRAY_SIZE(alc260_fujitsu_capture_sources),
+               .input_mux = alc260_fujitsu_capture_sources,
        },
        [ALC260_ACER] = {
                .mixers = { alc260_acer_mixer,
@@ -3723,7 +3906,8 @@ static struct alc_config_preset alc260_presets[] = {
                .adc_nids = alc260_dual_adc_nids,
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
-               .input_mux = &alc260_acer_capture_source,
+               .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources),
+               .input_mux = alc260_acer_capture_sources,
        },
 #ifdef CONFIG_SND_DEBUG
        [ALC260_TEST] = {
@@ -3736,7 +3920,8 @@ static struct alc_config_preset alc260_presets[] = {
                .adc_nids = alc260_test_adc_nids,
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
-               .input_mux = &alc260_test_capture_source,
+               .num_mux_defs = ARRAY_SIZE(alc260_test_capture_sources),
+               .input_mux = alc260_test_capture_sources,
        },
 #endif
 };
@@ -3828,7 +4013,6 @@ static struct hda_input_mux alc882_capture_source = {
                { "CD", 0x4 },
        },
 };
-
 #define alc882_mux_enum_info alc_mux_enum_info
 #define alc882_mux_enum_get alc_mux_enum_get
 
@@ -4730,6 +4914,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
                spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
 
        spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
+       spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
 
        return 1;
@@ -5406,6 +5591,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
 
        spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
 
+       spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
 
        spec->adc_nids = alc861_adc_nids;
index b56ca40..abe9493 100644 (file)
@@ -534,6 +534,22 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
+static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int pincap = snd_hda_param_read(codec, nid,
+                                                AC_PAR_PIN_CAP);
+       pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+       if (pincap & AC_PINCAP_VREF_100)
+               return AC_PINCTL_VREF_100;
+       if (pincap & AC_PINCAP_VREF_80)
+               return AC_PINCTL_VREF_80;
+       if (pincap & AC_PINCAP_VREF_50)
+               return AC_PINCTL_VREF_50;
+       if (pincap & AC_PINCAP_VREF_GRD)
+               return AC_PINCTL_VREF_GRD;
+       return 0;
+}
+
 static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
 
 {
@@ -571,9 +587,12 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
 
        if (val)
                stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
-       else
-               stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
-
+       else {
+               unsigned int pinctl = AC_PINCTL_IN_EN;
+               if (io_idx) /* set VREF for mic */
+                       pinctl |= stac92xx_get_vref(codec, nid);
+               stac92xx_auto_set_pinctl(codec, nid, pinctl);
+       }
         return 1;
 }
 
@@ -767,13 +786,8 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, struct auto_pin
                return 0;
 
        wid_caps = get_wcaps(codec, pin);
-       if (wid_caps & AC_WCAP_UNSOL_CAP) {
-               /* Enable unsolicited responses on the HP widget */
-               snd_hda_codec_write(codec, pin, 0,
-                               AC_VERB_SET_UNSOLICITED_ENABLE,
-                               STAC_UNSOL_ENABLE);
+       if (wid_caps & AC_WCAP_UNSOL_CAP)
                spec->hp_detect = 1;
-       }
 
        nid = snd_hda_codec_read(codec, pin, 0, AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
        for (i = 0; i < cfg->line_outs; i++) {
@@ -896,13 +910,8 @@ static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
                return 0;
 
        wid_caps = get_wcaps(codec, pin);
-       if (wid_caps & AC_WCAP_UNSOL_CAP) {
-               /* Enable unsolicited responses on the HP widget */
-               snd_hda_codec_write(codec, pin, 0,
-                               AC_VERB_SET_UNSOLICITED_ENABLE,
-                               STAC_UNSOL_ENABLE);
+       if (wid_caps & AC_WCAP_UNSOL_CAP)
                spec->hp_detect = 1;
-       }
 
        return 0;
 }
@@ -944,6 +953,10 @@ static int stac92xx_init(struct hda_codec *codec)
 
        /* set up pins */
        if (spec->hp_detect) {
+               /* Enable unsolicited responses on the HP widget */
+               snd_hda_codec_write(codec, cfg->hp_pin, 0,
+                               AC_VERB_SET_UNSOLICITED_ENABLE,
+                               STAC_UNSOL_ENABLE);
                /* fake event to set up pins */
                codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
        } else {
@@ -951,9 +964,13 @@ static int stac92xx_init(struct hda_codec *codec)
                stac92xx_auto_init_hp_out(codec);
        }
        for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (cfg->input_pins[i])
-                       stac92xx_auto_set_pinctl(codec, cfg->input_pins[i],
-                                                AC_PINCTL_IN_EN);
+               hda_nid_t nid = cfg->input_pins[i];
+               if (nid) {
+                       unsigned int pinctl = AC_PINCTL_IN_EN;
+                       if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
+                               pinctl |= stac92xx_get_vref(codec, nid);
+                       stac92xx_auto_set_pinctl(codec, nid, pinctl);
+               }
        }
        if (cfg->dig_out_pin)
                stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
index 7e6608b..336dc48 100644 (file)
 #define CS8415_C_BUFFER        0x20
 #define CS8415_ID              0x7F
 
-static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg, unsigned short val) {
+/* PCA9554 registers */
+#define PCA9554_DEV     0x40            /* I2C device address */
+#define PCA9554_IN      0x00            /* input port */
+#define PCA9554_OUT     0x01            /* output port */
+#define PCA9554_INVERT  0x02            /* input invert */
+#define PCA9554_DIR     0x03            /* port directions */
+
+/*
+ * Aureon Universe additional controls using PCA9554
+ */
+
+/*
+ * Send data to pca9554
+ */
+static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
+                                unsigned char data)
+{
+       unsigned int tmp;
+       int i, j;
+       unsigned char dev = PCA9554_DEV;  /* ID 0100000, write */
+       unsigned char val = 0;
+
+       tmp = snd_ice1712_gpio_read(ice);
+
+       snd_ice1712_gpio_set_mask(ice, ~(AUREON_SPI_MOSI|AUREON_SPI_CLK|
+                                        AUREON_WM_RW|AUREON_WM_CS|
+                                        AUREON_CS8415_CS));
+       tmp |= AUREON_WM_RW;
+       tmp |= AUREON_CS8415_CS | AUREON_WM_CS; /* disable SPI devices */
+
+       tmp &= ~AUREON_SPI_MOSI;
+       tmp &= ~AUREON_SPI_CLK;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(50);
+
+       /* 
+        * send i2c stop condition and start condition
+        * to obtain sane state
+        */
+       tmp |= AUREON_SPI_CLK;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(50);
+       tmp |= AUREON_SPI_MOSI;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(100);
+       tmp &= ~AUREON_SPI_MOSI;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(50);
+       tmp &= ~AUREON_SPI_CLK;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(100);
+       /*
+        * send device address, command and value,
+        * skipping ack cycles inbetween
+        */
+       for (j = 0; j < 3; j++) {
+               switch(j) {
+               case 0: val = dev; break;
+               case 1: val = reg; break;
+               case 2: val = data; break;
+               }
+               for (i = 7; i >= 0; i--) {
+                       tmp &= ~AUREON_SPI_CLK;
+                       snd_ice1712_gpio_write(ice, tmp);
+                       udelay(40);
+                       if (val & (1 << i))
+                               tmp |= AUREON_SPI_MOSI;
+                       else
+                               tmp &= ~AUREON_SPI_MOSI;
+                       snd_ice1712_gpio_write(ice, tmp);
+                       udelay(40);
+                       tmp |= AUREON_SPI_CLK;
+                       snd_ice1712_gpio_write(ice, tmp);
+                       udelay(40);
+               }
+                tmp &= ~AUREON_SPI_CLK;
+               snd_ice1712_gpio_write(ice, tmp);
+               udelay(40);
+               tmp |= AUREON_SPI_CLK;
+               snd_ice1712_gpio_write(ice, tmp);
+               udelay(40);
+               tmp &= ~AUREON_SPI_CLK;
+               snd_ice1712_gpio_write(ice, tmp);
+               udelay(40);
+       }
+       tmp &= ~AUREON_SPI_CLK;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(40);
+       tmp &= ~AUREON_SPI_MOSI;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(40);
+       tmp |= AUREON_SPI_CLK;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(50);
+       tmp |= AUREON_SPI_MOSI;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(100);
+}
+
+static int aureon_universe_inmux_info(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_info *uinfo)
+{
+       char *texts[3] = {"Internal Aux", "Wavetable", "Rear Line-In"};
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 3;
+       if(uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = ice->spec.aureon.pca9554_out;
+       return 0;
+}
+
+static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       unsigned char oval, nval;
+       int change;
+
+       snd_ice1712_save_gpio_status(ice);
+       
+       oval = ice->spec.aureon.pca9554_out;
+       nval = ucontrol->value.integer.value[0];
+       if ((change = (oval != nval))) {
+               aureon_pca9554_write(ice, PCA9554_OUT, nval);
+               ice->spec.aureon.pca9554_out = nval;
+       }
+       snd_ice1712_restore_gpio_status(ice);
+       
+       return change;
+}
+
+
+static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
+                             unsigned short val)
+{
        unsigned int tmp;
 
        /* Send address to XILINX chip */
@@ -146,7 +290,8 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r
 /*
  * Initialize STAC9744 chip
  */
-static int aureon_ac97_init (struct snd_ice1712 *ice) {
+static int aureon_ac97_init (struct snd_ice1712 *ice)
+{
        int i;
        static unsigned short ac97_defaults[] = {
                0x00, 0x9640,
@@ -1598,7 +1743,15 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
                .get = aureon_ac97_vol_get,
                .put = aureon_ac97_vol_put,
                .private_value = AC97_VIDEO|AUREON_AC97_STEREO
-       }
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Aux Source",
+               .info = aureon_universe_inmux_info,
+               .get = aureon_universe_inmux_get,
+               .put = aureon_universe_inmux_put
+       }
+
 };
 
        
@@ -1856,6 +2009,10 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
        }
 
        snd_ice1712_restore_gpio_status(ice);
+
+        /* initialize PCA9554 pin directions & set default input*/
+       aureon_pca9554_write(ice, PCA9554_DIR, 0x00);
+       aureon_pca9554_write(ice, PCA9554_OUT, 0x00);   /* internal AUX */
        
        ice->spec.aureon.master[0] = WM_VOL_MUTE;
        ice->spec.aureon.master[1] = WM_VOL_MUTE;
index b88eeba..32f8415 100644 (file)
@@ -2402,7 +2402,7 @@ static int __devinit snd_ice1712_chip_init(struct snd_ice1712 *ice)
        if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE && !ice->dxr_enable) {
                 /* Limit active ADCs and DACs to 6;  */
                 /* Note: DXR extension not supported */
-               pci_write_config_byte(ice->pci, 0x60, 0x0a);
+               pci_write_config_byte(ice->pci, 0x60, 0x2a);
        } else {
                pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]);
        }
index f9b22d4..053f8e5 100644 (file)
@@ -373,6 +373,7 @@ struct snd_ice1712 {
                        unsigned int cs8415_mux;
                        unsigned short master[2];
                        unsigned short vol[8];
+                       unsigned char pca9554_out;
                } aureon;
                /* AC97 register cache for Phase28 */
                struct phase28_spec {
index 44393e1..9c90d90 100644 (file)
@@ -831,8 +831,8 @@ struct snd_m3 {
        struct snd_pcm *pcm;
 
        struct pci_dev *pci;
-       struct m3_quirk *quirk;
-       struct m3_hv_quirk *hv_quirk;
+       const struct m3_quirk *quirk;
+       const struct m3_hv_quirk *hv_quirk;
 
        int dacs_active;
        int timer_users;
@@ -892,7 +892,7 @@ static struct pci_device_id snd_m3_ids[] = {
 
 MODULE_DEVICE_TABLE(pci, snd_m3_ids);
 
-static struct m3_quirk m3_quirk_list[] = {
+static const struct m3_quirk m3_quirk_list[] = {
        /* panasonic CF-28 "toughbook" */
        {
                .name = "Panasonic CF-28",
@@ -950,7 +950,7 @@ static struct m3_quirk m3_quirk_list[] = {
 };
 
 /* These values came from the Windows driver. */
-static struct m3_hv_quirk m3_hv_quirk_list[] = {
+static const struct m3_hv_quirk m3_hv_quirk_list[] = {
        /* Allegro chips */
        { 0x125D, 0x1988, 0x0E11, 0x002E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
        { 0x125D, 0x1988, 0x0E11, 0x0094, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
@@ -1361,7 +1361,7 @@ static void snd_m3_pcm_setup2(struct snd_m3 *chip, struct m3_dma *s,
 }
 
 
-static struct play_vals {
+static const struct play_vals {
        u16 addr, val;
 } pv[] = {
        {CDATA_LEFT_VOLUME, ARB_VOLUME},
@@ -1428,7 +1428,7 @@ snd_m3_playback_setup(struct snd_m3 *chip, struct m3_dma *s,
 /*
  *    Native record driver 
  */
-static struct rec_vals {
+static const struct rec_vals {
        u16 addr, val;
 } rv[] = {
        {CDATA_LEFT_VOLUME, ARB_VOLUME},
@@ -1598,12 +1598,26 @@ static void snd_m3_update_ptr(struct snd_m3 *chip, struct m3_dma *s)
        if (! s->running)
                return;
 
-       hwptr = snd_m3_get_pointer(chip, s, subs) % s->dma_size;
-       diff = (s->dma_size + hwptr - s->hwptr) % s->dma_size;
+       hwptr = snd_m3_get_pointer(chip, s, subs);
+
+       /* try to avoid expensive modulo divisions */
+       if (hwptr >= s->dma_size)
+               hwptr %= s->dma_size;
+
+       diff = s->dma_size + hwptr - s->hwptr;
+       if (diff >= s->dma_size)
+               diff %= s->dma_size;
+
        s->hwptr = hwptr;
        s->count += diff;
+
        if (s->count >= (signed)s->period_size) {
-               s->count %= s->period_size;
+
+               if (s->count < 2 * (signed)s->period_size)
+                       s->count -= (signed)s->period_size;
+               else
+                       s->count %= s->period_size;
+
                spin_unlock(&chip->reg_lock);
                snd_pcm_period_elapsed(subs);
                spin_lock(&chip->reg_lock);
@@ -1942,6 +1956,7 @@ static int snd_m3_ac97_wait(struct snd_m3 *chip)
        do {
                if (! (snd_m3_inb(chip, 0x30) & 1))
                        return 0;
+               cpu_relax();
        } while (i-- > 0);
 
        snd_printk(KERN_ERR "ac97 serial bus busy\n");
@@ -1953,16 +1968,18 @@ snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
 {
        struct snd_m3 *chip = ac97->private_data;
        unsigned long flags;
-       unsigned short data;
+       unsigned short data = 0xffff;
 
        if (snd_m3_ac97_wait(chip))
-               return 0xffff;
+               goto fail;
        spin_lock_irqsave(&chip->ac97_lock, flags);
        snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);
        if (snd_m3_ac97_wait(chip))
-               return 0xffff;
+               goto fail_unlock;
        data = snd_m3_inw(chip, CODEC_DATA);
+fail_unlock:
        spin_unlock_irqrestore(&chip->ac97_lock, flags);
+fail:
        return data;
 }
 
@@ -2121,7 +2138,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
  * DSP Code images
  */
 
-static u16 assp_kernel_image[] __devinitdata = {
+static const u16 assp_kernel_image[] __devinitdata = {
     0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, 
     0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 
     0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 
@@ -2208,7 +2225,7 @@ static u16 assp_kernel_image[] __devinitdata = {
  * Mini sample rate converter code image
  * that is to be loaded at 0x400 on the DSP.
  */
-static u16 assp_minisrc_image[] __devinitdata = {
+static const u16 assp_minisrc_image[] __devinitdata = {
 
     0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, 
     0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, 
@@ -2251,7 +2268,7 @@ static u16 assp_minisrc_image[] __devinitdata = {
  */
 
 #define MINISRC_LPF_LEN 10
-static u16 minisrc_lpf[MINISRC_LPF_LEN] __devinitdata = {
+static const u16 minisrc_lpf[MINISRC_LPF_LEN] __devinitdata = {
        0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C,
        0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F
 };
@@ -2358,7 +2375,7 @@ static int __devinit snd_m3_assp_client_init(struct snd_m3 *chip, struct m3_dma
         */
 
        /*
-        * align instance address to 256 bytes so that it's
+        * align instance address to 256 bytes so that its
         * shifted list address is aligned.
         * list address = (mem address >> 1) >> 7;
         */
@@ -2647,8 +2664,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
 {
        struct snd_m3 *chip;
        int i, err;
-       struct m3_quirk *quirk;
-       struct m3_hv_quirk *hv_quirk;
+       const struct m3_quirk *quirk;
+       const struct m3_hv_quirk *hv_quirk;
        static struct snd_device_ops ops = {
                .dev_free =     snd_m3_dev_free,
        };
@@ -2843,12 +2860,12 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
        }
 
 #if 0 /* TODO: not supported yet */
-       /* TODO enable midi irq and i/o */
+       /* TODO enable MIDI IRQ and I/O */
        err = snd_mpu401_uart_new(chip->card, 0, MPU401_HW_MPU401,
                                  chip->iobase + MPU401_DATA_PORT, 1,
                                  chip->irq, 0, &chip->rmidi);
        if (err < 0)
-               printk(KERN_WARNING "maestro3: no midi support.\n");
+               printk(KERN_WARNING "maestro3: no MIDI support.\n");
 #endif
 
        pci_set_drvdata(pci, card);
index fdc652c..c40f590 100644 (file)
@@ -274,12 +274,9 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
 
        /* test first xilinx */
        chipsc = PCXHR_INPL(mgr, PCXHR_PLX_CHIPSC);
-       if (!second) {
-               if (chipsc & PCXHR_CHIPSC_GPI_USERI) {
-                       snd_printdd("no need to load first xilinx\n");
-                       return 0; /* first xilinx is already present and cannot be reset */
-               }
-       } else {
+       /* REV01 cards do not support the PCXHR_CHIPSC_GPI_USERI bit anymore */
+       /* this bit will always be 1; no possibility to test presence of first xilinx */
+       if(second) {
                if ((chipsc & PCXHR_CHIPSC_GPI_USERI) == 0) {
                        snd_printk(KERN_ERR "error loading first xilinx\n");
                        return -EINVAL;
diff --git a/sound/pci/riptide/Makefile b/sound/pci/riptide/Makefile
new file mode 100644 (file)
index 0000000..dcd2e64
--- /dev/null
@@ -0,0 +1,3 @@
+snd-riptide-objs := riptide.o
+
+obj-$(CONFIG_SND_RIPTIDE) += snd-riptide.o
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
new file mode 100644 (file)
index 0000000..f148ee4
--- /dev/null
@@ -0,0 +1,2223 @@
+/*
+ *   Driver for the Conexant Riptide Soundchip
+ *
+ *     Copyright (c) 2004 Peter Gruber <nokos@gmx.net>
+ *
+ *   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
+ *
+ */
+/*
+  History:
+   - 02/15/2004 first release
+   
+  This Driver is based on the OSS Driver version from Linuxant (riptide-0.6lnxtbeta03111100)
+  credits from the original files:
+  
+  MODULE NAME:        cnxt_rt.h                       
+  AUTHOR:             K. Lazarev  (Transcribed by KNL)
+  HISTORY:         Major Revision               Date        By
+            -----------------------------     --------     -----
+            Created                           02/1/2000     KNL
+
+  MODULE NAME:     int_mdl.c                       
+  AUTHOR:          Konstantin Lazarev    (Transcribed by KNL)
+  HISTORY:         Major Revision               Date        By
+            -----------------------------     --------     -----
+            Created                           10/01/99      KNL
+           
+  MODULE NAME:        riptide.h                       
+  AUTHOR:             O. Druzhinin  (Transcribed by OLD)
+  HISTORY:         Major Revision               Date        By
+            -----------------------------     --------     -----
+            Created                           10/16/97      OLD
+
+  MODULE NAME:        Rp_Cmdif.cpp                       
+  AUTHOR:             O. Druzhinin  (Transcribed by OLD)
+                      K. Lazarev    (Transcribed by KNL)
+  HISTORY:         Major Revision               Date        By
+            -----------------------------     --------     -----
+            Adopted from NT4 driver            6/22/99      OLD
+            Ported to Linux                    9/01/99      KNL
+
+  MODULE NAME:        rt_hw.c                       
+  AUTHOR:             O. Druzhinin  (Transcribed by OLD)
+                      C. Lazarev    (Transcribed by CNL)
+  HISTORY:         Major Revision               Date        By
+            -----------------------------     --------     -----
+            Created                           11/18/97      OLD
+            Hardware functions for RipTide    11/24/97      CNL
+            (ES1) are coded
+            Hardware functions for RipTide    12/24/97      CNL
+            (A0) are coded
+            Hardware functions for RipTide    03/20/98      CNL
+            (A1) are coded
+            Boot loader is included           05/07/98      CNL
+            Redesigned for WDM                07/27/98      CNL
+            Redesigned for Linux              09/01/99      CNL
+
+  MODULE NAME:        rt_hw.h
+  AUTHOR:             C. Lazarev    (Transcribed by CNL)
+  HISTORY:         Major Revision               Date        By
+            -----------------------------     --------     -----
+            Created                           11/18/97      CNL
+
+  MODULE NAME:     rt_mdl.c                       
+  AUTHOR:          Konstantin Lazarev    (Transcribed by KNL)
+  HISTORY:         Major Revision               Date        By
+            -----------------------------     --------     -----
+            Created                           10/01/99      KNL
+
+  MODULE NAME:        mixer.h                        
+  AUTHOR:             K. Kenney
+  HISTORY:         Major Revision                   Date          By
+            -----------------------------          --------     -----
+            Created from MS W95 Sample             11/28/95      KRS
+            RipTide                                10/15/97      KRS
+            Adopted for Windows NT driver          01/20/98      CNL
+*/
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/gameport.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/initval.h>
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
+MODULE_AUTHOR("Peter Gruber <nokos@gmx.net>");
+MODULE_DESCRIPTION("riptide");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Conexant,Riptide}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+
+#ifdef SUPPORT_JOYSTICK
+static int joystick_port[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS - 1)] = 0x200 };
+#endif
+static int mpu_port[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS - 1)] = 0x330 };
+static int opl3_port[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS - 1)] = 0x388 };
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Riptide soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Riptide soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Riptide soundcard.");
+#ifdef SUPPORT_JOYSTICK
+module_param_array(joystick_port, int, NULL, 0444);
+MODULE_PARM_DESC(joystick_port, "Joystick port # for Riptide soundcard.");
+#endif
+module_param_array(mpu_port, int, NULL, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU401 port # for Riptide driver.");
+module_param_array(opl3_port, int, NULL, 0444);
+MODULE_PARM_DESC(opl3_port, "OPL3 port # for Riptide driver.");
+
+/*
+ */
+
+#define MPU401_HW_RIPTIDE MPU401_HW_MPU401
+#define OPL3_HW_RIPTIDE   OPL3_HW_OPL3
+
+#define PCI_EXT_CapId       0x40
+#define PCI_EXT_NextCapPrt  0x41
+#define PCI_EXT_PWMC        0x42
+#define PCI_EXT_PWSCR       0x44
+#define PCI_EXT_Data00      0x46
+#define PCI_EXT_PMSCR_BSE   0x47
+#define PCI_EXT_SB_Base     0x48
+#define PCI_EXT_FM_Base     0x4a
+#define PCI_EXT_MPU_Base    0x4C
+#define PCI_EXT_Game_Base   0x4E
+#define PCI_EXT_Legacy_Mask 0x50
+#define PCI_EXT_AsicRev     0x52
+#define PCI_EXT_Reserved3   0x53
+
+#define LEGACY_ENABLE_ALL      0x8000  /* legacy device options */
+#define LEGACY_ENABLE_SB       0x4000
+#define LEGACY_ENABLE_FM       0x2000
+#define LEGACY_ENABLE_MPU_INT  0x1000
+#define LEGACY_ENABLE_MPU      0x0800
+#define LEGACY_ENABLE_GAMEPORT 0x0400
+
+#define MAX_WRITE_RETRY  10    /* cmd interface limits */
+#define MAX_ERROR_COUNT  10
+#define CMDIF_TIMEOUT    500000
+#define RESET_TRIES      5
+
+#define READ_PORT_ULONG(p)     inl((unsigned long)&(p))
+#define WRITE_PORT_ULONG(p,x)  outl(x,(unsigned long)&(p))
+
+#define READ_AUDIO_CONTROL(p)     READ_PORT_ULONG(p->audio_control)
+#define WRITE_AUDIO_CONTROL(p,x)  WRITE_PORT_ULONG(p->audio_control,x)
+#define UMASK_AUDIO_CONTROL(p,x)  WRITE_PORT_ULONG(p->audio_control,READ_PORT_ULONG(p->audio_control)|x)
+#define MASK_AUDIO_CONTROL(p,x)   WRITE_PORT_ULONG(p->audio_control,READ_PORT_ULONG(p->audio_control)&x)
+#define READ_AUDIO_STATUS(p)      READ_PORT_ULONG(p->audio_status)
+
+#define SET_GRESET(p)     UMASK_AUDIO_CONTROL(p,0x0001)        /* global reset switch */
+#define UNSET_GRESET(p)   MASK_AUDIO_CONTROL(p,~0x0001)
+#define SET_AIE(p)        UMASK_AUDIO_CONTROL(p,0x0004)        /* interrupt enable */
+#define UNSET_AIE(p)      MASK_AUDIO_CONTROL(p,~0x0004)
+#define SET_AIACK(p)      UMASK_AUDIO_CONTROL(p,0x0008)        /* interrupt acknowledge */
+#define UNSET_AIACKT(p)   MASKAUDIO_CONTROL(p,~0x0008)
+#define SET_ECMDAE(p)     UMASK_AUDIO_CONTROL(p,0x0010)
+#define UNSET_ECMDAE(p)   MASK_AUDIO_CONTROL(p,~0x0010)
+#define SET_ECMDBE(p)     UMASK_AUDIO_CONTROL(p,0x0020)
+#define UNSET_ECMDBE(p)   MASK_AUDIO_CONTROL(p,~0x0020)
+#define SET_EDATAF(p)     UMASK_AUDIO_CONTROL(p,0x0040)
+#define UNSET_EDATAF(p)   MASK_AUDIO_CONTROL(p,~0x0040)
+#define SET_EDATBF(p)     UMASK_AUDIO_CONTROL(p,0x0080)
+#define UNSET_EDATBF(p)   MASK_AUDIO_CONTROL(p,~0x0080)
+#define SET_ESBIRQON(p)   UMASK_AUDIO_CONTROL(p,0x0100)
+#define UNSET_ESBIRQON(p) MASK_AUDIO_CONTROL(p,~0x0100)
+#define SET_EMPUIRQ(p)    UMASK_AUDIO_CONTROL(p,0x0200)
+#define UNSET_EMPUIRQ(p)  MASK_AUDIO_CONTROL(p,~0x0200)
+#define IS_CMDE(a)        (READ_PORT_ULONG(a->stat)&0x1)       /* cmd empty */
+#define IS_DATF(a)        (READ_PORT_ULONG(a->stat)&0x2)       /* data filled */
+#define IS_READY(p)       (READ_AUDIO_STATUS(p)&0x0001)
+#define IS_DLREADY(p)     (READ_AUDIO_STATUS(p)&0x0002)
+#define IS_DLERR(p)       (READ_AUDIO_STATUS(p)&0x0004)
+#define IS_GERR(p)        (READ_AUDIO_STATUS(p)&0x0008)        /* error ! */
+#define IS_CMDAEIRQ(p)    (READ_AUDIO_STATUS(p)&0x0010)
+#define IS_CMDBEIRQ(p)    (READ_AUDIO_STATUS(p)&0x0020)
+#define IS_DATAFIRQ(p)    (READ_AUDIO_STATUS(p)&0x0040)
+#define IS_DATBFIRQ(p)    (READ_AUDIO_STATUS(p)&0x0080)
+#define IS_EOBIRQ(p)      (READ_AUDIO_STATUS(p)&0x0100)        /* interrupt status */
+#define IS_EOSIRQ(p)      (READ_AUDIO_STATUS(p)&0x0200)
+#define IS_EOCIRQ(p)      (READ_AUDIO_STATUS(p)&0x0400)
+#define IS_UNSLIRQ(p)     (READ_AUDIO_STATUS(p)&0x0800)
+#define IS_SBIRQ(p)       (READ_AUDIO_STATUS(p)&0x1000)
+#define IS_MPUIRQ(p)      (READ_AUDIO_STATUS(p)&0x2000)
+
+#define RESP 0x00000001                /* command flags */
+#define PARM 0x00000002
+#define CMDA 0x00000004
+#define CMDB 0x00000008
+#define NILL 0x00000000
+
+#define LONG0(a)   ((u32)a)    /* shifts and masks */
+#define BYTE0(a)   (LONG0(a)&0xff)
+#define BYTE1(a)   (BYTE0(a)<<8)
+#define BYTE2(a)   (BYTE0(a)<<16)
+#define BYTE3(a)   (BYTE0(a)<<24)
+#define WORD0(a)   (LONG0(a)&0xffff)
+#define WORD1(a)   (WORD0(a)<<8)
+#define WORD2(a)   (WORD0(a)<<16)
+#define TRINIB0(a) (LONG0(a)&0xffffff)
+#define TRINIB1(a) (TRINIB0(a)<<8)
+
+#define RET(a)     ((union cmdret *)(a))
+
+#define SEND_GETV(p,b)             sendcmd(p,RESP,GETV,0,RET(b))       /* get version */
+#define SEND_GETC(p,b,c)           sendcmd(p,PARM|RESP,GETC,c,RET(b))
+#define SEND_GUNS(p,b)             sendcmd(p,RESP,GUNS,0,RET(b))
+#define SEND_SCID(p,b)             sendcmd(p,RESP,SCID,0,RET(b))
+#define SEND_RMEM(p,b,c,d)         sendcmd(p,PARM|RESP,RMEM|BYTE1(b),LONG0(c),RET(d))  /* memory access for firmware write */
+#define SEND_SMEM(p,b,c)           sendcmd(p,PARM,SMEM|BYTE1(b),LONG0(c),RET(0))       /* memory access for firmware write */
+#define SEND_WMEM(p,b,c)           sendcmd(p,PARM,WMEM|BYTE1(b),LONG0(c),RET(0))       /* memory access for firmware write */
+#define SEND_SDTM(p,b,c)           sendcmd(p,PARM|RESP,SDTM|TRINIB1(b),0,RET(c))       /* memory access for firmware write */
+#define SEND_GOTO(p,b)             sendcmd(p,PARM,GOTO,LONG0(b),RET(0))        /* memory access for firmware write */
+#define SEND_SETDPLL(p)                   sendcmd(p,0,ARM_SETDPLL,0,RET(0))
+#define SEND_SSTR(p,b,c)           sendcmd(p,PARM,SSTR|BYTE3(b),LONG0(c),RET(0))       /* start stream */
+#define SEND_PSTR(p,b)             sendcmd(p,PARM,PSTR,BYTE3(b),RET(0))        /* pause stream */
+#define SEND_KSTR(p,b)             sendcmd(p,PARM,KSTR,BYTE3(b),RET(0))        /* stop stream */
+#define SEND_KDMA(p)               sendcmd(p,0,KDMA,0,RET(0))  /* stop all dma */
+#define SEND_GPOS(p,b,c,d)         sendcmd(p,PARM|RESP,GPOS,BYTE3(c)|BYTE2(b),RET(d))  /* get position in dma */
+#define SEND_SETF(p,b,c,d,e,f,g)   sendcmd(p,PARM,SETF|WORD1(b)|BYTE3(c),d|BYTE1(e)|BYTE2(f)|BYTE3(g),RET(0))  /* set sample format at mixer */
+#define SEND_GSTS(p,b,c,d)         sendcmd(p,PARM|RESP,GSTS,BYTE3(c)|BYTE2(b),RET(d))
+#define SEND_NGPOS(p,b,c,d)        sendcmd(p,PARM|RESP,NGPOS,BYTE3(c)|BYTE2(b),RET(d))
+#define SEND_PSEL(p,b,c)           sendcmd(p,PARM,PSEL,BYTE2(b)|BYTE3(c),RET(0))       /* activate lbus path */
+#define SEND_PCLR(p,b,c)           sendcmd(p,PARM,PCLR,BYTE2(b)|BYTE3(c),RET(0))       /* deactivate lbus path */
+#define SEND_PLST(p,b)             sendcmd(p,PARM,PLST,BYTE3(b),RET(0))
+#define SEND_RSSV(p,b,c,d)         sendcmd(p,PARM|RESP,RSSV,BYTE2(b)|BYTE3(c),RET(d))
+#define SEND_LSEL(p,b,c,d,e,f,g,h) sendcmd(p,PARM,LSEL|BYTE1(b)|BYTE2(c)|BYTE3(d),BYTE0(e)|BYTE1(f)|BYTE2(g)|BYTE3(h),RET(0))  /* select paths for internal connections */
+#define SEND_SSRC(p,b,c,d,e)       sendcmd(p,PARM,SSRC|BYTE1(b)|WORD2(c),WORD0(d)|WORD2(e),RET(0))     /* configure source */
+#define SEND_SLST(p,b)             sendcmd(p,PARM,SLST,BYTE3(b),RET(0))
+#define SEND_RSRC(p,b,c)           sendcmd(p,RESP,RSRC|BYTE1(b),0,RET(c))      /* read source config */
+#define SEND_SSRB(p,b,c)           sendcmd(p,PARM,SSRB|BYTE1(b),WORD2(c),RET(0))
+#define SEND_SDGV(p,b,c,d,e)       sendcmd(p,PARM,SDGV|BYTE2(b)|BYTE3(c),WORD0(d)|WORD2(e),RET(0))     /* set digital mixer */
+#define SEND_RDGV(p,b,c,d)         sendcmd(p,PARM|RESP,RDGV|BYTE2(b)|BYTE3(c),0,RET(d))        /* read digital mixer */
+#define SEND_DLST(p,b)             sendcmd(p,PARM,DLST,BYTE3(b),RET(0))
+#define SEND_SACR(p,b,c)           sendcmd(p,PARM,SACR,WORD0(b)|WORD2(c),RET(0))       /* set AC97 register */
+#define SEND_RACR(p,b,c)           sendcmd(p,PARM|RESP,RACR,WORD2(b),RET(c))   /* get AC97 register */
+#define SEND_ALST(p,b)             sendcmd(p,PARM,ALST,BYTE3(b),RET(0))
+#define SEND_TXAC(p,b,c,d,e,f)     sendcmd(p,PARM,TXAC|BYTE1(b)|WORD2(c),WORD0(d)|BYTE2(e)|BYTE3(f),RET(0))
+#define SEND_RXAC(p,b,c,d)         sendcmd(p,PARM|RESP,RXAC,BYTE2(b)|BYTE3(c),RET(d))
+#define SEND_SI2S(p,b)             sendcmd(p,PARM,SI2S,WORD2(b),RET(0))
+
+#define EOB_STATUS         0x80000000  /* status flags : block boundary */
+#define EOS_STATUS         0x40000000  /*              : stoppped */
+#define EOC_STATUS         0x20000000  /*              : stream end */
+#define ERR_STATUS         0x10000000
+#define EMPTY_STATUS       0x08000000
+
+#define IEOB_ENABLE        0x1 /* enable interrupts for status notification above */
+#define IEOS_ENABLE        0x2
+#define IEOC_ENABLE        0x4
+#define RDONCE             0x8
+#define DESC_MAX_MASK      0xff
+
+#define ST_PLAY  0x1           /* stream states */
+#define ST_STOP  0x2
+#define ST_PAUSE 0x4
+
+#define I2S_INTDEC     3       /* config for I2S link */
+#define I2S_MERGER     0
+#define I2S_SPLITTER   0
+#define I2S_MIXER      7
+#define I2S_RATE       44100
+
+#define MODEM_INTDEC   4       /* config for modem link */
+#define MODEM_MERGER   3
+#define MODEM_SPLITTER 0
+#define MODEM_MIXER    11
+
+#define FM_INTDEC      3       /* config for FM/OPL3 link */
+#define FM_MERGER      0
+#define FM_SPLITTER    0
+#define FM_MIXER       9
+
+#define SPLIT_PATH  0x80       /* path splitting flag */
+
+enum FIRMWARE {
+       DATA_REC = 0, EXT_END_OF_FILE, EXT_SEG_ADDR_REC, EXT_GOTO_CMD_REC,
+       EXT_LIN_ADDR_REC,
+};
+
+enum CMDS {
+       GETV = 0x00, GETC, GUNS, SCID, RMEM =
+           0x10, SMEM, WMEM, SDTM, GOTO, SSTR =
+           0x20, PSTR, KSTR, KDMA, GPOS, SETF, GSTS, NGPOS, PSEL =
+           0x30, PCLR, PLST, RSSV, LSEL, SSRC = 0x40, SLST, RSRC, SSRB, SDGV =
+           0x50, RDGV, DLST, SACR = 0x60, RACR, ALST, TXAC, RXAC, SI2S =
+           0x70, ARM_SETDPLL = 0x72,
+};
+
+enum E1SOURCE {
+       ARM2LBUS_FIFO0 = 0, ARM2LBUS_FIFO1, ARM2LBUS_FIFO2, ARM2LBUS_FIFO3,
+       ARM2LBUS_FIFO4, ARM2LBUS_FIFO5, ARM2LBUS_FIFO6, ARM2LBUS_FIFO7,
+       ARM2LBUS_FIFO8, ARM2LBUS_FIFO9, ARM2LBUS_FIFO10, ARM2LBUS_FIFO11,
+       ARM2LBUS_FIFO12, ARM2LBUS_FIFO13, ARM2LBUS_FIFO14, ARM2LBUS_FIFO15,
+       INTER0_OUT, INTER1_OUT, INTER2_OUT, INTER3_OUT, INTER4_OUT,
+       INTERM0_OUT, INTERM1_OUT, INTERM2_OUT, INTERM3_OUT, INTERM4_OUT,
+       INTERM5_OUT, INTERM6_OUT, DECIMM0_OUT, DECIMM1_OUT, DECIMM2_OUT,
+       DECIMM3_OUT, DECIM0_OUT, SR3_4_OUT, OPL3_SAMPLE, ASRC0, ASRC1,
+       ACLNK2PADC, ACLNK2MODEM0RX, ACLNK2MIC, ACLNK2MODEM1RX, ACLNK2HNDMIC,
+       DIGITAL_MIXER_OUT0, GAINFUNC0_OUT, GAINFUNC1_OUT, GAINFUNC2_OUT,
+       GAINFUNC3_OUT, GAINFUNC4_OUT, SOFTMODEMTX, SPLITTER0_OUTL,
+       SPLITTER0_OUTR, SPLITTER1_OUTL, SPLITTER1_OUTR, SPLITTER2_OUTL,
+       SPLITTER2_OUTR, SPLITTER3_OUTL, SPLITTER3_OUTR, MERGER0_OUT,
+       MERGER1_OUT, MERGER2_OUT, MERGER3_OUT, ARM2LBUS_FIFO_DIRECT, NO_OUT
+};
+
+enum E2SINK {
+       LBUS2ARM_FIFO0 = 0, LBUS2ARM_FIFO1, LBUS2ARM_FIFO2, LBUS2ARM_FIFO3,
+       LBUS2ARM_FIFO4, LBUS2ARM_FIFO5, LBUS2ARM_FIFO6, LBUS2ARM_FIFO7,
+       INTER0_IN, INTER1_IN, INTER2_IN, INTER3_IN, INTER4_IN, INTERM0_IN,
+       INTERM1_IN, INTERM2_IN, INTERM3_IN, INTERM4_IN, INTERM5_IN, INTERM6_IN,
+       DECIMM0_IN, DECIMM1_IN, DECIMM2_IN, DECIMM3_IN, DECIM0_IN, SR3_4_IN,
+       PDAC2ACLNK, MODEM0TX2ACLNK, MODEM1TX2ACLNK, HNDSPK2ACLNK,
+       DIGITAL_MIXER_IN0, DIGITAL_MIXER_IN1, DIGITAL_MIXER_IN2,
+       DIGITAL_MIXER_IN3, DIGITAL_MIXER_IN4, DIGITAL_MIXER_IN5,
+       DIGITAL_MIXER_IN6, DIGITAL_MIXER_IN7, DIGITAL_MIXER_IN8,
+       DIGITAL_MIXER_IN9, DIGITAL_MIXER_IN10, DIGITAL_MIXER_IN11,
+       GAINFUNC0_IN, GAINFUNC1_IN, GAINFUNC2_IN, GAINFUNC3_IN, GAINFUNC4_IN,
+       SOFTMODEMRX, SPLITTER0_IN, SPLITTER1_IN, SPLITTER2_IN, SPLITTER3_IN,
+       MERGER0_INL, MERGER0_INR, MERGER1_INL, MERGER1_INR, MERGER2_INL,
+       MERGER2_INR, MERGER3_INL, MERGER3_INR, E2SINK_MAX
+};
+
+enum LBUS_SINK {
+       LS_SRC_INTERPOLATOR = 0, LS_SRC_INTERPOLATORM, LS_SRC_DECIMATOR,
+       LS_SRC_DECIMATORM, LS_MIXER_IN, LS_MIXER_GAIN_FUNCTION,
+       LS_SRC_SPLITTER, LS_SRC_MERGER, LS_NONE1, LS_NONE2,
+};
+
+enum RT_CHANNEL_IDS {
+       M0TX = 0, M1TX, TAMTX, HSSPKR, PDAC, DSNDTX0, DSNDTX1, DSNDTX2,
+       DSNDTX3, DSNDTX4, DSNDTX5, DSNDTX6, DSNDTX7, WVSTRTX, COP3DTX, SPARE,
+       M0RX, HSMIC, M1RX, CLEANRX, MICADC, PADC, COPRX1, COPRX2,
+       CHANNEL_ID_COUNTER
+};
+
+enum { SB_CMD = 0, MODEM_CMD, I2S_CMD0, I2S_CMD1, FM_CMD, MAX_CMD };
+
+struct lbuspath {
+       unsigned char *noconv;
+       unsigned char *stereo;
+       unsigned char *mono;
+};
+
+struct cmdport {
+       u32 data1;              /* cmd,param */
+       u32 data2;              /* param */
+       u32 stat;               /* status */
+       u32 pad[5];
+};
+
+struct riptideport {
+       u32 audio_control;      /* status registers */
+       u32 audio_status;
+       u32 pad[2];
+       struct cmdport port[2]; /* command ports */
+};
+
+struct cmdif {
+       struct riptideport *hwport;
+       spinlock_t lock;
+       unsigned int cmdcnt;    /* cmd statistics */
+       unsigned int cmdtime;
+       unsigned int cmdtimemax;
+       unsigned int cmdtimemin;
+       unsigned int errcnt;
+       int is_reset;
+};
+
+struct riptide_firmware {
+       u16 ASIC;
+       u16 CODEC;
+       u16 AUXDSP;
+       u16 PROG;
+};
+
+union cmdret {
+       u8 retbytes[8];
+       u16 retwords[4];
+       u32 retlongs[2];
+};
+
+union firmware_version {
+       union cmdret ret;
+       struct riptide_firmware firmware;
+};
+
+#define get_pcmhwdev(substream) (struct pcmhw *)(substream->runtime->private_data)
+
+#define PLAYBACK_SUBSTREAMS 3
+struct snd_riptide {
+       struct snd_card *card;
+       struct pci_dev *pci;
+       const struct firmware *fw_entry;
+
+       struct cmdif *cif;
+
+       struct snd_pcm *pcm;
+       struct snd_pcm *pcm_i2s;
+       struct snd_rawmidi *rmidi;
+       struct snd_opl3 *opl3;
+       struct snd_ac97 *ac97;
+       struct snd_ac97_bus *ac97_bus;
+
+       struct snd_pcm_substream *playback_substream[PLAYBACK_SUBSTREAMS];
+       struct snd_pcm_substream *capture_substream;
+
+       int openstreams;
+
+       int irq;
+       unsigned long port;
+       unsigned short mpuaddr;
+       unsigned short opladdr;
+#ifdef SUPPORT_JOYSTICK
+       unsigned short gameaddr;
+#endif
+       struct resource *res_port;
+
+       unsigned short device_id;
+
+       union firmware_version firmware;
+
+       spinlock_t lock;
+       struct tasklet_struct riptide_tq;
+       struct snd_info_entry *proc_entry;
+
+       unsigned long received_irqs;
+       unsigned long handled_irqs;
+#ifdef CONFIG_PM
+       int in_suspend;
+#endif
+};
+
+struct sgd {                   /* scatter gather desriptor */
+       u32 dwNextLink;
+       u32 dwSegPtrPhys;
+       u32 dwSegLen;
+       u32 dwStat_Ctl;
+};
+
+struct pcmhw {                 /* pcm descriptor */
+       struct lbuspath paths;
+       unsigned char *lbuspath;
+       unsigned char source;
+       unsigned char intdec[2];
+       unsigned char mixer;
+       unsigned char id;
+       unsigned char state;
+       unsigned int rate;
+       unsigned int channels;
+       snd_pcm_format_t format;
+       struct snd_dma_buffer sgdlist;
+       struct sgd *sgdbuf;
+       unsigned int size;
+       unsigned int pages;
+       unsigned int oldpos;
+       unsigned int pointer;
+};
+
+#define CMDRET_ZERO (union cmdret){{(u32)0, (u32) 0}}
+
+static int sendcmd(struct cmdif *cif, u32 flags, u32 cmd, u32 parm,
+                  union cmdret *ret);
+static int getsourcesink(struct cmdif *cif, unsigned char source,
+                        unsigned char sink, unsigned char *a,
+                        unsigned char *b);
+static int snd_riptide_initialize(struct snd_riptide *chip);
+static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip);
+
+/*
+ */
+
+static struct pci_device_id snd_riptide_ids[] = {
+       {
+        .vendor = 0x127a,.device = 0x4310,
+        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
+        },
+       {
+        .vendor = 0x127a,.device = 0x4320,
+        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
+        },
+       {
+        .vendor = 0x127a,.device = 0x4330,
+        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
+        },
+       {
+        .vendor = 0x127a,.device = 0x4340,
+        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
+        },
+       {0,},
+};
+
+#ifdef SUPPORT_JOYSTICK
+static struct pci_device_id snd_riptide_joystick_ids[] = {
+       {
+        .vendor = 0x127a,.device = 0x4312,
+        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
+        },
+       {
+        .vendor = 0x127a,.device = 0x4322,
+        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
+        },
+       {.vendor = 0x127a,.device = 0x4332,
+        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
+        },
+       {.vendor = 0x127a,.device = 0x4342,
+        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
+        },
+       {0,},
+};
+#endif
+
+MODULE_DEVICE_TABLE(pci, snd_riptide_ids);
+
+/*
+ */
+
+static unsigned char lbusin2out[E2SINK_MAX + 1][2] = {
+       {NO_OUT, LS_NONE1}, {NO_OUT, LS_NONE2}, {NO_OUT, LS_NONE1}, {NO_OUT,
+                                                                    LS_NONE2},
+       {NO_OUT, LS_NONE1}, {NO_OUT, LS_NONE2}, {NO_OUT, LS_NONE1}, {NO_OUT,
+                                                                    LS_NONE2},
+       {INTER0_OUT, LS_SRC_INTERPOLATOR}, {INTER1_OUT, LS_SRC_INTERPOLATOR},
+       {INTER2_OUT, LS_SRC_INTERPOLATOR}, {INTER3_OUT, LS_SRC_INTERPOLATOR},
+       {INTER4_OUT, LS_SRC_INTERPOLATOR}, {INTERM0_OUT, LS_SRC_INTERPOLATORM},
+       {INTERM1_OUT, LS_SRC_INTERPOLATORM}, {INTERM2_OUT,
+                                             LS_SRC_INTERPOLATORM},
+       {INTERM3_OUT, LS_SRC_INTERPOLATORM}, {INTERM4_OUT,
+                                             LS_SRC_INTERPOLATORM},
+       {INTERM5_OUT, LS_SRC_INTERPOLATORM}, {INTERM6_OUT,
+                                             LS_SRC_INTERPOLATORM},
+       {DECIMM0_OUT, LS_SRC_DECIMATORM}, {DECIMM1_OUT, LS_SRC_DECIMATORM},
+       {DECIMM2_OUT, LS_SRC_DECIMATORM}, {DECIMM3_OUT, LS_SRC_DECIMATORM},
+       {DECIM0_OUT, LS_SRC_DECIMATOR}, {SR3_4_OUT, LS_NONE1}, {NO_OUT,
+                                                               LS_NONE2},
+       {NO_OUT, LS_NONE1}, {NO_OUT, LS_NONE2}, {NO_OUT, LS_NONE1},
+       {DIGITAL_MIXER_OUT0, LS_MIXER_IN}, {DIGITAL_MIXER_OUT0, LS_MIXER_IN},
+       {DIGITAL_MIXER_OUT0, LS_MIXER_IN}, {DIGITAL_MIXER_OUT0, LS_MIXER_IN},
+       {DIGITAL_MIXER_OUT0, LS_MIXER_IN}, {DIGITAL_MIXER_OUT0, LS_MIXER_IN},
+       {DIGITAL_MIXER_OUT0, LS_MIXER_IN}, {DIGITAL_MIXER_OUT0, LS_MIXER_IN},
+       {DIGITAL_MIXER_OUT0, LS_MIXER_IN}, {DIGITAL_MIXER_OUT0, LS_MIXER_IN},
+       {DIGITAL_MIXER_OUT0, LS_MIXER_IN}, {DIGITAL_MIXER_OUT0, LS_MIXER_IN},
+       {GAINFUNC0_OUT, LS_MIXER_GAIN_FUNCTION}, {GAINFUNC1_OUT,
+                                                 LS_MIXER_GAIN_FUNCTION},
+       {GAINFUNC2_OUT, LS_MIXER_GAIN_FUNCTION}, {GAINFUNC3_OUT,
+                                                 LS_MIXER_GAIN_FUNCTION},
+       {GAINFUNC4_OUT, LS_MIXER_GAIN_FUNCTION}, {SOFTMODEMTX, LS_NONE1},
+       {SPLITTER0_OUTL, LS_SRC_SPLITTER}, {SPLITTER1_OUTL, LS_SRC_SPLITTER},
+       {SPLITTER2_OUTL, LS_SRC_SPLITTER}, {SPLITTER3_OUTL, LS_SRC_SPLITTER},
+       {MERGER0_OUT, LS_SRC_MERGER}, {MERGER0_OUT, LS_SRC_MERGER},
+       {MERGER1_OUT, LS_SRC_MERGER},
+       {MERGER1_OUT, LS_SRC_MERGER}, {MERGER2_OUT, LS_SRC_MERGER},
+       {MERGER2_OUT, LS_SRC_MERGER},
+       {MERGER3_OUT, LS_SRC_MERGER}, {MERGER3_OUT, LS_SRC_MERGER}, {NO_OUT,
+                                                                    LS_NONE2},
+};
+
+static unsigned char lbus_play_opl3[] = {
+       DIGITAL_MIXER_IN0 + FM_MIXER, 0xff
+};
+static unsigned char lbus_play_modem[] = {
+       DIGITAL_MIXER_IN0 + MODEM_MIXER, 0xff
+};
+static unsigned char lbus_play_i2s[] = {
+       INTER0_IN + I2S_INTDEC, DIGITAL_MIXER_IN0 + I2S_MIXER, 0xff
+};
+static unsigned char lbus_play_out[] = {
+       PDAC2ACLNK, 0xff
+};
+static unsigned char lbus_play_outhp[] = {
+       HNDSPK2ACLNK, 0xff
+};
+static unsigned char lbus_play_noconv1[] = {
+       DIGITAL_MIXER_IN0, 0xff
+};
+static unsigned char lbus_play_stereo1[] = {
+       INTER0_IN, DIGITAL_MIXER_IN0, 0xff
+};
+static unsigned char lbus_play_mono1[] = {
+       INTERM0_IN, DIGITAL_MIXER_IN0, 0xff
+};
+static unsigned char lbus_play_noconv2[] = {
+       DIGITAL_MIXER_IN1, 0xff
+};
+static unsigned char lbus_play_stereo2[] = {
+       INTER1_IN, DIGITAL_MIXER_IN1, 0xff
+};
+static unsigned char lbus_play_mono2[] = {
+       INTERM1_IN, DIGITAL_MIXER_IN1, 0xff
+};
+static unsigned char lbus_play_noconv3[] = {
+       DIGITAL_MIXER_IN2, 0xff
+};
+static unsigned char lbus_play_stereo3[] = {
+       INTER2_IN, DIGITAL_MIXER_IN2, 0xff
+};
+static unsigned char lbus_play_mono3[] = {
+       INTERM2_IN, DIGITAL_MIXER_IN2, 0xff
+};
+static unsigned char lbus_rec_noconv1[] = {
+       LBUS2ARM_FIFO5, 0xff
+};
+static unsigned char lbus_rec_stereo1[] = {
+       DECIM0_IN, LBUS2ARM_FIFO5, 0xff
+};
+static unsigned char lbus_rec_mono1[] = {
+       DECIMM3_IN, LBUS2ARM_FIFO5, 0xff
+};
+
+static unsigned char play_ids[] = { 4, 1, 2, };
+static unsigned char play_sources[] = {
+       ARM2LBUS_FIFO4, ARM2LBUS_FIFO1, ARM2LBUS_FIFO2,
+};
+static struct lbuspath lbus_play_paths[] = {
+       {
+        .noconv = lbus_play_noconv1,
+        .stereo = lbus_play_stereo1,
+        .mono = lbus_play_mono1,
+        },
+       {
+        .noconv = lbus_play_noconv2,
+        .stereo = lbus_play_stereo2,
+        .mono = lbus_play_mono2,
+        },
+       {
+        .noconv = lbus_play_noconv3,
+        .stereo = lbus_play_stereo3,
+        .mono = lbus_play_mono3,
+        },
+};
+static struct lbuspath lbus_rec_path = {
+       .noconv = lbus_rec_noconv1,
+       .stereo = lbus_rec_stereo1,
+       .mono = lbus_rec_mono1,
+};
+
+#define FIRMWARE_VERSIONS 1
+static union firmware_version firmware_versions[] = {
+       {
+        .firmware.ASIC = 3,.firmware.CODEC = 2,
+        .firmware.AUXDSP = 3,.firmware.PROG = 773,
+        },
+};
+
+static u32 atoh(unsigned char *in, unsigned int len)
+{
+       u32 sum = 0;
+       unsigned int mult = 1;
+       unsigned char c;
+
+       while (len) {
+               c = in[len - 1];
+               if ((c >= '0') && (c <= '9'))
+                       sum += mult * (c - '0');
+               else if ((c >= 'A') && (c <= 'F'))
+                       sum += mult * (c - ('A' - 10));
+               else if ((c >= 'a') && (c <= 'f'))
+                       sum += mult * (c - ('a' - 10));
+               mult *= 16;
+               --len;
+       }
+       return sum;
+}
+
+static int senddata(struct cmdif *cif, unsigned char *in, u32 offset)
+{
+       u32 addr;
+       u32 data;
+       u32 i;
+       unsigned char *p;
+
+       i = atoh(&in[1], 2);
+       addr = offset + atoh(&in[3], 4);
+       if (SEND_SMEM(cif, 0, addr) != 0)
+               return -EACCES;
+       p = in + 9;
+       while (i) {
+               data = atoh(p, 8);
+               if (SEND_WMEM(cif, 2,
+                             ((data & 0x0f0f0f0f) << 4) | ((data & 0xf0f0f0f0)
+                                                           >> 4)))
+                       return -EACCES;
+               i -= 4;
+               p += 8;
+       }
+       return 0;
+}
+
+static int loadfirmware(struct cmdif *cif, unsigned char *img,
+                       unsigned int size)
+{
+       unsigned char *in;
+       u32 laddr, saddr, t, val;
+       int err = 0;
+
+       laddr = saddr = 0;
+       while (size > 0 && err == 0) {
+               in = img;
+               if (in[0] == ':') {
+                       t = atoh(&in[7], 2);
+                       switch (t) {
+                       case DATA_REC:
+                               err = senddata(cif, in, laddr + saddr);
+                               break;
+                       case EXT_SEG_ADDR_REC:
+                               saddr = atoh(&in[9], 4) << 4;
+                               break;
+                       case EXT_LIN_ADDR_REC:
+                               laddr = atoh(&in[9], 4) << 16;
+                               break;
+                       case EXT_GOTO_CMD_REC:
+                               val = atoh(&in[9], 8);
+                               if (SEND_GOTO(cif, val) != 0)
+                                       err = -EACCES;
+                               break;
+                       case EXT_END_OF_FILE:
+                               size = 0;
+                               break;
+                       default:
+                               break;
+                       }
+                       while (size > 0) {
+                               size--;
+                               if (*img++ == '\n')
+                                       break;
+                       }
+               }
+       }
+       snd_printdd("load firmware return %d\n", err);
+       return err;
+}
+
+static void
+alloclbuspath(struct cmdif *cif, unsigned char source,
+             unsigned char *path, unsigned char *mixer, unsigned char *s)
+{
+       while (*path != 0xff) {
+               unsigned char sink, type;
+
+               sink = *path & (~SPLIT_PATH);
+               if (sink != E2SINK_MAX) {
+                       snd_printdd("alloc path 0x%x->0x%x\n", source, sink);
+                       SEND_PSEL(cif, source, sink);
+                       source = lbusin2out[sink][0];
+                       type = lbusin2out[sink][1];
+                       if (type == LS_MIXER_IN) {
+                               if (mixer)
+                                       *mixer = sink - DIGITAL_MIXER_IN0;
+                       }
+                       if (type == LS_SRC_DECIMATORM ||
+                           type == LS_SRC_DECIMATOR ||
+                           type == LS_SRC_INTERPOLATORM ||
+                           type == LS_SRC_INTERPOLATOR) {
+                               if (s) {
+                                       if (s[0] != 0xff)
+                                               s[1] = sink;
+                                       else
+                                               s[0] = sink;
+                               }
+                       }
+               }
+               if (*path++ & SPLIT_PATH) {
+                       unsigned char *npath = path;
+
+                       while (*npath != 0xff)
+                               npath++;
+                       alloclbuspath(cif, source + 1, ++npath, mixer, s);
+               }
+       }
+}
+
+static void
+freelbuspath(struct cmdif *cif, unsigned char source, unsigned char *path)
+{
+       while (*path != 0xff) {
+               unsigned char sink;
+
+               sink = *path & (~SPLIT_PATH);
+               if (sink != E2SINK_MAX) {
+                       snd_printdd("free path 0x%x->0x%x\n", source, sink);
+                       SEND_PCLR(cif, source, sink);
+                       source = lbusin2out[sink][0];
+               }
+               if (*path++ & SPLIT_PATH) {
+                       unsigned char *npath = path;
+
+                       while (*npath != 0xff)
+                               npath++;
+                       freelbuspath(cif, source + 1, ++npath);
+               }
+       }
+}
+
+static int writearm(struct cmdif *cif, u32 addr, u32 data, u32 mask)
+{
+       union cmdret rptr = CMDRET_ZERO;
+       unsigned int i = MAX_WRITE_RETRY;
+       int flag = 1;
+
+       SEND_RMEM(cif, 0x02, addr, &rptr);
+       rptr.retlongs[0] &= (~mask);
+
+       while (--i) {
+               SEND_SMEM(cif, 0x01, addr);
+               SEND_WMEM(cif, 0x02, (rptr.retlongs[0] | data));
+               SEND_RMEM(cif, 0x02, addr, &rptr);
+               if ((rptr.retlongs[0] & data) == data) {
+                       flag = 0;
+                       break;
+               } else
+                       rptr.retlongs[0] &= ~mask;
+       }
+       snd_printdd("send arm 0x%x 0x%x 0x%x return %d\n", addr, data, mask,
+                   flag);
+       return flag;
+}
+
+static int sendcmd(struct cmdif *cif, u32 flags, u32 cmd, u32 parm,
+                  union cmdret *ret)
+{
+       int i, j;
+       int err;
+       unsigned int time = 0;
+       unsigned long irqflags;
+       struct riptideport *hwport;
+       struct cmdport *cmdport = NULL;
+
+       snd_assert(cif, return -EINVAL);
+
+       hwport = cif->hwport;
+       if (cif->errcnt > MAX_ERROR_COUNT) {
+               if (cif->is_reset) {
+                       snd_printk(KERN_ERR
+                                  "Riptide: Too many failed cmds, reinitializing\n");
+                       if (riptide_reset(cif, NULL) == 0) {
+                               cif->errcnt = 0;
+                               return -EIO;
+                       }
+               }
+               snd_printk(KERN_ERR "Riptide: Initialization failed.\n");
+               return -EINVAL;
+       }
+       if (ret) {
+               ret->retlongs[0] = 0;
+               ret->retlongs[1] = 0;
+       }
+       i = 0;
+       spin_lock_irqsave(&cif->lock, irqflags);
+       while (i++ < CMDIF_TIMEOUT && !IS_READY(cif->hwport))
+               udelay(10);
+       if (i >= CMDIF_TIMEOUT) {
+               err = -EBUSY;
+               goto errout;
+       }
+
+       err = 0;
+       for (j = 0, time = 0; time < CMDIF_TIMEOUT; j++, time += 2) {
+               cmdport = &(hwport->port[j % 2]);
+               if (IS_DATF(cmdport)) { /* free pending data */
+                       READ_PORT_ULONG(cmdport->data1);
+                       READ_PORT_ULONG(cmdport->data2);
+               }
+               if (IS_CMDE(cmdport)) {
+                       if (flags & PARM)       /* put data */
+                               WRITE_PORT_ULONG(cmdport->data2, parm);
+                       WRITE_PORT_ULONG(cmdport->data1, cmd);  /* write cmd */
+                       if ((flags & RESP) && ret) {
+                               while (!IS_DATF(cmdport) &&
+                                      time++ < CMDIF_TIMEOUT)
+                                       udelay(10);
+                               if (time < CMDIF_TIMEOUT) {     /* read response */
+                                       ret->retlongs[0] =
+                                           READ_PORT_ULONG(cmdport->data1);
+                                       ret->retlongs[1] =
+                                           READ_PORT_ULONG(cmdport->data2);
+                               } else {
+                                       err = -ENOSYS;
+                                       goto errout;
+                               }
+                       }
+                       break;
+               }
+               udelay(20);
+       }
+       if (time == CMDIF_TIMEOUT) {
+               err = -ENODATA;
+               goto errout;
+       }
+       spin_unlock_irqrestore(&cif->lock, irqflags);
+
+       cif->cmdcnt++;          /* update command statistics */
+       cif->cmdtime += time;
+       if (time > cif->cmdtimemax)
+               cif->cmdtimemax = time;
+       if (time < cif->cmdtimemin)
+               cif->cmdtimemin = time;
+       if ((cif->cmdcnt) % 1000 == 0)
+               snd_printdd
+                   ("send cmd %d time: %d mintime: %d maxtime %d err: %d\n",
+                    cif->cmdcnt, cif->cmdtime, cif->cmdtimemin,
+                    cif->cmdtimemax, cif->errcnt);
+       return 0;
+
+      errout:
+       cif->errcnt++;
+       spin_unlock_irqrestore(&cif->lock, irqflags);
+       snd_printdd
+           ("send cmd %d hw: 0x%x flag: 0x%x cmd: 0x%x parm: 0x%x ret: 0x%x 0x%x CMDE: %d DATF: %d failed %d\n",
+            cif->cmdcnt, (int)((void *)&(cmdport->stat) - (void *)hwport),
+            flags, cmd, parm, ret ? ret->retlongs[0] : 0,
+            ret ? ret->retlongs[1] : 0, IS_CMDE(cmdport), IS_DATF(cmdport),
+            err);
+       return err;
+}
+
+static int
+setmixer(struct cmdif *cif, short num, unsigned short rval, unsigned short lval)
+{
+       union cmdret rptr = CMDRET_ZERO;
+       int i = 0;
+
+       snd_printdd("sent mixer %d: 0x%d 0x%d\n", num, rval, lval);
+       do {
+               SEND_SDGV(cif, num, num, rval, lval);
+               SEND_RDGV(cif, num, num, &rptr);
+               if (rptr.retwords[0] == lval && rptr.retwords[1] == rval)
+                       return 0;
+       } while (i++ < MAX_WRITE_RETRY);
+       snd_printdd("sent mixer failed\n");
+       return -EIO;
+}
+
+static int getpaths(struct cmdif *cif, unsigned char *o)
+{
+       unsigned char src[E2SINK_MAX];
+       unsigned char sink[E2SINK_MAX];
+       int i, j = 0;
+
+       for (i = 0; i < E2SINK_MAX; i++) {
+               getsourcesink(cif, i, i, &src[i], &sink[i]);
+               if (sink[i] < E2SINK_MAX) {
+                       o[j++] = sink[i];
+                       o[j++] = i;
+               }
+       }
+       return j;
+}
+
+static int
+getsourcesink(struct cmdif *cif, unsigned char source, unsigned char sink,
+             unsigned char *a, unsigned char *b)
+{
+       union cmdret rptr = CMDRET_ZERO;
+
+       if (SEND_RSSV(cif, source, sink, &rptr) &&
+           SEND_RSSV(cif, source, sink, &rptr))
+               return -EIO;
+       *a = rptr.retbytes[0];
+       *b = rptr.retbytes[1];
+       snd_printdd("getsourcesink 0x%x 0x%x\n", *a, *b);
+       return 0;
+}
+
+static int
+getsamplerate(struct cmdif *cif, unsigned char *intdec, unsigned int *rate)
+{
+       unsigned char *s;
+       unsigned int p[2] = { 0, 0 };
+       int i;
+       union cmdret rptr = CMDRET_ZERO;
+
+       s = intdec;
+       for (i = 0; i < 2; i++) {
+               if (*s != 0xff) {
+                       if (SEND_RSRC(cif, *s, &rptr) &&
+                           SEND_RSRC(cif, *s, &rptr))
+                               return -EIO;
+                       p[i] += rptr.retwords[1];
+                       p[i] *= rptr.retwords[2];
+                       p[i] += rptr.retwords[3];
+                       p[i] /= 65536;
+               }
+               s++;
+       }
+       if (p[0]) {
+               if (p[1] != p[0])
+                       snd_printdd("rates differ %d %d\n", p[0], p[1]);
+               *rate = (unsigned int)p[0];
+       } else
+               *rate = (unsigned int)p[1];
+       snd_printdd("getsampleformat %d %d %d\n", intdec[0], intdec[1], *rate);
+       return 0;
+}
+
+static int
+setsampleformat(struct cmdif *cif,
+               unsigned char mixer, unsigned char id,
+               unsigned char channels, unsigned char format)
+{
+       unsigned char w, ch, sig, order;
+
+       snd_printdd
+           ("setsampleformat mixer: %d id: %d channels: %d format: %d\n",
+            mixer, id, channels, format);
+       ch = channels == 1;
+       w = snd_pcm_format_width(format) == 8;
+       sig = snd_pcm_format_unsigned(format) != 0;
+       order = snd_pcm_format_big_endian(format) != 0;
+
+       if (SEND_SETF(cif, mixer, w, ch, order, sig, id) &&
+           SEND_SETF(cif, mixer, w, ch, order, sig, id)) {
+               snd_printdd("setsampleformat failed\n");
+               return -EIO;
+       }
+       return 0;
+}
+
+static int
+setsamplerate(struct cmdif *cif, unsigned char *intdec, unsigned int rate)
+{
+       u32 D, M, N;
+       union cmdret rptr = CMDRET_ZERO;
+       int i;
+
+       snd_printdd("setsamplerate intdec: %d,%d rate: %d\n", intdec[0],
+                   intdec[1], rate);
+       D = 48000;
+       M = ((rate == 48000) ? 47999 : rate) * 65536;
+       N = M % D;
+       M /= D;
+       for (i = 0; i < 2; i++) {
+               if (*intdec != 0xff) {
+                       do {
+                               SEND_SSRC(cif, *intdec, D, M, N);
+                               SEND_RSRC(cif, *intdec, &rptr);
+                       } while (rptr.retwords[1] != D &&
+                                rptr.retwords[2] != M &&
+                                rptr.retwords[3] != N &&
+                                i++ < MAX_WRITE_RETRY);
+                       if (i == MAX_WRITE_RETRY) {
+                               snd_printdd("sent samplerate %d: %d failed\n",
+                                           *intdec, rate);
+                               return -EIO;
+                       }
+               }
+               intdec++;
+       }
+       return 0;
+}
+
+static int
+getmixer(struct cmdif *cif, short num, unsigned short *rval,
+        unsigned short *lval)
+{
+       union cmdret rptr = CMDRET_ZERO;
+
+       if (SEND_RDGV(cif, num, num, &rptr) && SEND_RDGV(cif, num, num, &rptr))
+               return -EIO;
+       *rval = rptr.retwords[0];
+       *lval = rptr.retwords[1];
+       snd_printdd("got mixer %d: 0x%d 0x%d\n", num, *rval, *lval);
+       return 0;
+}
+
+static void riptide_handleirq(unsigned long dev_id)
+{
+       struct snd_riptide *chip = (void *)dev_id;
+       struct cmdif *cif = chip->cif;
+       struct snd_pcm_substream *substream[PLAYBACK_SUBSTREAMS + 1];
+       struct snd_pcm_runtime *runtime;
+       struct pcmhw *data = NULL;
+       unsigned int pos, period_bytes;
+       struct sgd *c;
+       int i, j;
+       unsigned int flag;
+
+       if (!cif)
+               return;
+
+       for (i = 0; i < PLAYBACK_SUBSTREAMS; i++)
+               substream[i] = chip->playback_substream[i];
+       substream[i] = chip->capture_substream;
+       for (i = 0; i < PLAYBACK_SUBSTREAMS + 1; i++) {
+               if (substream[i] &&
+                   (runtime = substream[i]->runtime) &&
+                   (data = runtime->private_data) && data->state != ST_STOP) {
+                       pos = 0;
+                       for (j = 0; j < data->pages; j++) {
+                               c = &data->sgdbuf[j];
+                               flag = le32_to_cpu(c->dwStat_Ctl);
+                               if (flag & EOB_STATUS)
+                                       pos += le32_to_cpu(c->dwSegLen);
+                               if (flag & EOC_STATUS)
+                                       pos += le32_to_cpu(c->dwSegLen);
+                               if ((flag & EOS_STATUS)
+                                   && (data->state == ST_PLAY)) {
+                                       data->state = ST_STOP;
+                                       snd_printk(KERN_ERR
+                                                  "Riptide: DMA stopped unexpectedly\n");
+                               }
+                               c->dwStat_Ctl =
+                                   cpu_to_le32(flag &
+                                               ~(EOS_STATUS | EOB_STATUS |
+                                                 EOC_STATUS));
+                       }
+                       data->pointer += pos;
+                       pos += data->oldpos;
+                       if (data->state != ST_STOP) {
+                               period_bytes =
+                                   frames_to_bytes(runtime,
+                                                   runtime->period_size);
+                               snd_printdd
+                                   ("interrupt 0x%x after 0x%lx of 0x%lx frames in period\n",
+                                    READ_AUDIO_STATUS(cif->hwport),
+                                    bytes_to_frames(runtime, pos),
+                                    runtime->period_size);
+                               j = 0;
+                               if (pos >= period_bytes) {
+                                       j++;
+                                       while (pos >= period_bytes)
+                                               pos -= period_bytes;
+                               }
+                               data->oldpos = pos;
+                               if (j > 0)
+                                       snd_pcm_period_elapsed(substream[i]);
+                       }
+               }
+       }
+}
+
+#ifdef CONFIG_PM
+static int riptide_suspend(struct pci_dev *pci, pm_message_t state)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct snd_riptide *chip = card->private_data;
+
+       chip->in_suspend = 1;
+       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+       snd_pcm_suspend_all(chip->pcm);
+       snd_ac97_suspend(chip->ac97);
+       pci_set_power_state(pci, PCI_D3hot);
+       pci_disable_device(pci);
+       pci_save_state(pci);
+       return 0;
+}
+
+static int riptide_resume(struct pci_dev *pci)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct snd_riptide *chip = card->private_data;
+
+       pci_restore_state(pci);
+       pci_enable_device(pci);
+       pci_set_power_state(pci, PCI_D0);
+       pci_set_master(pci);
+       snd_riptide_initialize(chip);
+       snd_ac97_resume(chip->ac97);
+       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+       chip->in_suspend = 0;
+       return 0;
+}
+#endif
+
+static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip)
+{
+       int timeout, tries;
+       union cmdret rptr = CMDRET_ZERO;
+       union firmware_version firmware;
+       int i, j, err, has_firmware;
+
+       if (!cif)
+               return -EINVAL;
+
+       cif->cmdcnt = 0;
+       cif->cmdtime = 0;
+       cif->cmdtimemax = 0;
+       cif->cmdtimemin = 0xffffffff;
+       cif->errcnt = 0;
+       cif->is_reset = 0;
+
+       tries = RESET_TRIES;
+       has_firmware = 0;
+       while (has_firmware == 0 && tries-- > 0) {
+               for (i = 0; i < 2; i++) {
+                       WRITE_PORT_ULONG(cif->hwport->port[i].data1, 0);
+                       WRITE_PORT_ULONG(cif->hwport->port[i].data2, 0);
+               }
+               SET_GRESET(cif->hwport);
+               udelay(100);
+               UNSET_GRESET(cif->hwport);
+               udelay(100);
+
+               for (timeout = 100000; --timeout; udelay(10)) {
+                       if (IS_READY(cif->hwport) && !IS_GERR(cif->hwport))
+                               break;
+               }
+               if (timeout == 0) {
+                       snd_printk(KERN_ERR
+                                  "Riptide: device not ready, audio status: 0x%x ready: %d gerr: %d\n",
+                                  READ_AUDIO_STATUS(cif->hwport),
+                                  IS_READY(cif->hwport), IS_GERR(cif->hwport));
+                       return -EIO;
+               } else {
+                       snd_printdd
+                           ("Riptide: audio status: 0x%x ready: %d gerr: %d\n",
+                            READ_AUDIO_STATUS(cif->hwport),
+                            IS_READY(cif->hwport), IS_GERR(cif->hwport));
+               }
+
+               SEND_GETV(cif, &rptr);
+               for (i = 0; i < 4; i++)
+                       firmware.ret.retwords[i] = rptr.retwords[i];
+
+               snd_printdd
+                   ("Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n",
+                    firmware.firmware.ASIC, firmware.firmware.CODEC,
+                    firmware.firmware.AUXDSP, firmware.firmware.PROG);
+
+               for (j = 0; j < FIRMWARE_VERSIONS; j++) {
+                       has_firmware = 1;
+                       for (i = 0; i < 4; i++) {
+                               if (firmware_versions[j].ret.retwords[i] !=
+                                   firmware.ret.retwords[i])
+                                       has_firmware = 0;
+                       }
+                       if (has_firmware)
+                               break;
+               }
+
+               if (chip != NULL && has_firmware == 0) {
+                       snd_printdd("Writing Firmware\n");
+                       if (!chip->fw_entry) {
+                               if ((err =
+                                    request_firmware(&chip->fw_entry,
+                                                     "riptide.hex",
+                                                     &chip->pci->dev)) != 0) {
+                                       snd_printk(KERN_ERR
+                                                  "Riptide: Firmware not available %d\n",
+                                                  err);
+                                       return -EIO;
+                               }
+                       }
+                       err = loadfirmware(cif, chip->fw_entry->data,
+                                          chip->fw_entry->size);
+                       if (err)
+                               snd_printk(KERN_ERR
+                                          "Riptide: Could not load firmware %d\n",
+                                          err);
+               }
+       }
+
+       SEND_SACR(cif, 0, AC97_RESET);
+       SEND_RACR(cif, AC97_RESET, &rptr);
+       snd_printdd("AC97: 0x%x 0x%x\n", rptr.retlongs[0], rptr.retlongs[1]);
+
+       SEND_PLST(cif, 0);
+       SEND_SLST(cif, 0);
+       SEND_DLST(cif, 0);
+       SEND_ALST(cif, 0);
+       SEND_KDMA(cif);
+
+       writearm(cif, 0x301F8, 1, 1);
+       writearm(cif, 0x301F4, 1, 1);
+
+       SEND_LSEL(cif, MODEM_CMD, 0, 0, MODEM_INTDEC, MODEM_MERGER,
+                 MODEM_SPLITTER, MODEM_MIXER);
+       setmixer(cif, MODEM_MIXER, 0x7fff, 0x7fff);
+       alloclbuspath(cif, ARM2LBUS_FIFO13, lbus_play_modem, NULL, NULL);
+
+       SEND_LSEL(cif, FM_CMD, 0, 0, FM_INTDEC, FM_MERGER, FM_SPLITTER,
+                 FM_MIXER);
+       setmixer(cif, FM_MIXER, 0x7fff, 0x7fff);
+       writearm(cif, 0x30648 + FM_MIXER * 4, 0x01, 0x00000005);
+       writearm(cif, 0x301A8, 0x02, 0x00000002);
+       writearm(cif, 0x30264, 0x08, 0xffffffff);
+       alloclbuspath(cif, OPL3_SAMPLE, lbus_play_opl3, NULL, NULL);
+
+       SEND_SSRC(cif, I2S_INTDEC, 48000,
+                 ((u32) I2S_RATE * 65536) / 48000,
+                 ((u32) I2S_RATE * 65536) % 48000);
+       SEND_LSEL(cif, I2S_CMD0, 0, 0, I2S_INTDEC, I2S_MERGER, I2S_SPLITTER,
+                 I2S_MIXER);
+       SEND_SI2S(cif, 1);
+       alloclbuspath(cif, ARM2LBUS_FIFO0, lbus_play_i2s, NULL, NULL);
+       alloclbuspath(cif, DIGITAL_MIXER_OUT0, lbus_play_out, NULL, NULL);
+       alloclbuspath(cif, DIGITAL_MIXER_OUT0, lbus_play_outhp, NULL, NULL);
+
+       SET_AIACK(cif->hwport);
+       SET_AIE(cif->hwport);
+       SET_AIACK(cif->hwport);
+       cif->is_reset = 1;
+       if (chip) {
+               for (i = 0; i < 4; i++)
+                       chip->firmware.ret.retwords[i] =
+                           firmware.ret.retwords[i];
+       }
+
+       return 0;
+}
+
+static struct snd_pcm_hardware snd_riptide_playback = {
+       .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =
+           SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8
+           | SNDRV_PCM_FMTBIT_U16_LE,
+       .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+       .rate_min = 5500,
+       .rate_max = 48000,
+       .channels_min = 1,
+       .channels_max = 2,
+       .buffer_bytes_max = (64 * 1024),
+       .period_bytes_min = PAGE_SIZE >> 1,
+       .period_bytes_max = PAGE_SIZE << 8,
+       .periods_min = 2,
+       .periods_max = 64,
+       .fifo_size = 0,
+};
+static struct snd_pcm_hardware snd_riptide_capture = {
+       .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =
+           SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8
+           | SNDRV_PCM_FMTBIT_U16_LE,
+       .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+       .rate_min = 5500,
+       .rate_max = 48000,
+       .channels_min = 1,
+       .channels_max = 2,
+       .buffer_bytes_max = (64 * 1024),
+       .period_bytes_min = PAGE_SIZE >> 1,
+       .period_bytes_max = PAGE_SIZE << 3,
+       .periods_min = 2,
+       .periods_max = 64,
+       .fifo_size = 0,
+};
+
+static snd_pcm_uframes_t snd_riptide_pointer(struct snd_pcm_substream
+                                            *substream)
+{
+       struct snd_riptide *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct pcmhw *data = get_pcmhwdev(substream);
+       struct cmdif *cif = chip->cif;
+       union cmdret rptr = CMDRET_ZERO;
+       snd_pcm_uframes_t ret;
+
+       SEND_GPOS(cif, 0, data->id, &rptr);
+       if (data->size && runtime->period_size) {
+               snd_printdd
+                   ("pointer stream %d position 0x%x(0x%x in buffer) bytes 0x%lx(0x%lx in period) frames\n",
+                    data->id, rptr.retlongs[1], rptr.retlongs[1] % data->size,
+                    bytes_to_frames(runtime, rptr.retlongs[1]),
+                    bytes_to_frames(runtime,
+                                    rptr.retlongs[1]) % runtime->period_size);
+               if (rptr.retlongs[1] > data->pointer)
+                       ret =
+                           bytes_to_frames(runtime,
+                                           rptr.retlongs[1] % data->size);
+               else
+                       ret =
+                           bytes_to_frames(runtime,
+                                           data->pointer % data->size);
+       } else {
+               snd_printdd("stream not started or strange parms (%d %ld)\n",
+                           data->size, runtime->period_size);
+               ret = bytes_to_frames(runtime, 0);
+       }
+       return ret;
+}
+
+static int snd_riptide_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       int i, j;
+       struct snd_riptide *chip = snd_pcm_substream_chip(substream);
+       struct pcmhw *data = get_pcmhwdev(substream);
+       struct cmdif *cif = chip->cif;
+       union cmdret rptr = CMDRET_ZERO;
+
+       spin_lock(&chip->lock);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               if (!(data->state & ST_PLAY)) {
+                       SEND_SSTR(cif, data->id, data->sgdlist.addr);
+                       SET_AIE(cif->hwport);
+                       data->state = ST_PLAY;
+                       if (data->mixer != 0xff)
+                               setmixer(cif, data->mixer, 0x7fff, 0x7fff);
+                       chip->openstreams++;
+                       data->oldpos = 0;
+                       data->pointer = 0;
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               if (data->mixer != 0xff)
+                       setmixer(cif, data->mixer, 0, 0);
+               setmixer(cif, data->mixer, 0, 0);
+               SEND_KSTR(cif, data->id);
+               data->state = ST_STOP;
+               chip->openstreams--;
+               j = 0;
+               do {
+                       i = rptr.retlongs[1];
+                       SEND_GPOS(cif, 0, data->id, &rptr);
+                       udelay(1);
+               } while (i != rptr.retlongs[1] && j++ < MAX_WRITE_RETRY);
+               if (j >= MAX_WRITE_RETRY)
+                       snd_printk(KERN_ERR "Riptide: Could not stop stream!");
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (!(data->state & ST_PAUSE)) {
+                       SEND_PSTR(cif, data->id);
+                       data->state |= ST_PAUSE;
+                       chip->openstreams--;
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (data->state & ST_PAUSE) {
+                       SEND_SSTR(cif, data->id, data->sgdlist.addr);
+                       data->state &= ~ST_PAUSE;
+                       chip->openstreams++;
+               }
+               break;
+       default:
+               spin_unlock(&chip->lock);
+               return -EINVAL;
+       }
+       spin_unlock(&chip->lock);
+       return 0;
+}
+
+static int snd_riptide_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_riptide *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+       struct pcmhw *data = get_pcmhwdev(substream);
+       struct cmdif *cif = chip->cif;
+       unsigned char *lbuspath = NULL;
+       unsigned int rate, channels;
+       int err = 0;
+       snd_pcm_format_t format;
+
+       snd_assert(cif && data, return -EINVAL);
+
+       snd_printdd("prepare id %d ch: %d f:0x%x r:%d\n", data->id,
+                   runtime->channels, runtime->format, runtime->rate);
+
+       spin_lock_irq(&chip->lock);
+       channels = runtime->channels;
+       format = runtime->format;
+       rate = runtime->rate;
+       switch (channels) {
+       case 1:
+               if (rate == 48000 && format == SNDRV_PCM_FORMAT_S16_LE)
+                       lbuspath = data->paths.noconv;
+               else
+                       lbuspath = data->paths.mono;
+               break;
+       case 2:
+               if (rate == 48000 && format == SNDRV_PCM_FORMAT_S16_LE)
+                       lbuspath = data->paths.noconv;
+               else
+                       lbuspath = data->paths.stereo;
+               break;
+       }
+       snd_printdd("use sgdlist at 0x%p and buffer at 0x%p\n",
+                   data->sgdlist.area, sgbuf);
+       if (data->sgdlist.area && sgbuf) {
+               unsigned int i, j, size, pages, f, pt, period;
+               struct sgd *c, *p = NULL;
+
+               size = frames_to_bytes(runtime, runtime->buffer_size);
+               period = frames_to_bytes(runtime, runtime->period_size);
+               f = PAGE_SIZE;
+               while ((size + (f >> 1) - 1) <= (f << 7) && (f << 1) > period)
+                       f = f >> 1;
+               pages = (size + f - 1) / f;
+               data->size = size;
+               data->pages = pages;
+               snd_printdd
+                   ("create sgd size: 0x%x pages %d of size 0x%x for period 0x%x\n",
+                    size, pages, f, period);
+               pt = 0;
+               j = 0;
+               for (i = 0; i < pages; i++) {
+                       c = &data->sgdbuf[i];
+                       if (p)
+                               p->dwNextLink = cpu_to_le32(data->sgdlist.addr +
+                                                           (i *
+                                                            sizeof(struct
+                                                                   sgd)));
+                       c->dwNextLink = cpu_to_le32(data->sgdlist.addr);
+                       c->dwSegPtrPhys =
+                           cpu_to_le32(sgbuf->table[j].addr + pt);
+                       pt = (pt + f) % PAGE_SIZE;
+                       if (pt == 0)
+                               j++;
+                       c->dwSegLen = cpu_to_le32(f);
+                       c->dwStat_Ctl =
+                           cpu_to_le32(IEOB_ENABLE | IEOS_ENABLE |
+                                       IEOC_ENABLE);
+                       p = c;
+                       size -= f;
+               }
+               data->sgdbuf[i].dwSegLen = cpu_to_le32(size);
+       }
+       if (lbuspath && lbuspath != data->lbuspath) {
+               if (data->lbuspath)
+                       freelbuspath(cif, data->source, data->lbuspath);
+               alloclbuspath(cif, data->source, lbuspath,
+                             &data->mixer, data->intdec);
+               data->lbuspath = lbuspath;
+               data->rate = 0;
+       }
+       if (data->rate != rate || data->format != format ||
+           data->channels != channels) {
+               data->rate = rate;
+               data->format = format;
+               data->channels = channels;
+               if (setsampleformat
+                   (cif, data->mixer, data->id, channels, format)
+                   || setsamplerate(cif, data->intdec, rate))
+                       err = -EIO;
+       }
+       spin_unlock_irq(&chip->lock);
+       return err;
+}
+
+static int
+snd_riptide_hw_params(struct snd_pcm_substream *substream,
+                     struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_riptide *chip = snd_pcm_substream_chip(substream);
+       struct pcmhw *data = get_pcmhwdev(substream);
+       struct snd_dma_buffer *sgdlist = &data->sgdlist;
+       int err;
+
+       snd_printdd("hw params id %d (sgdlist: 0x%p 0x%lx %d)\n", data->id,
+                   sgdlist->area, (unsigned long)sgdlist->addr,
+                   (int)sgdlist->bytes);
+       if (sgdlist->area)
+               snd_dma_free_pages(sgdlist);
+       if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+                                      snd_dma_pci_data(chip->pci),
+                                      sizeof(struct sgd) * (DESC_MAX_MASK + 1),
+                                      sgdlist)) < 0) {
+               snd_printk(KERN_ERR "Riptide: failed to alloc %d dma bytes\n",
+                          (int)sizeof(struct sgd) * (DESC_MAX_MASK + 1));
+               return err;
+       }
+       data->sgdbuf = (struct sgd *)sgdlist->area;
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+static int snd_riptide_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_riptide *chip = snd_pcm_substream_chip(substream);
+       struct pcmhw *data = get_pcmhwdev(substream);
+       struct cmdif *cif = chip->cif;
+
+       if (cif && data) {
+               if (data->lbuspath)
+                       freelbuspath(cif, data->source, data->lbuspath);
+               data->lbuspath = NULL;
+               data->source = 0xff;
+               data->intdec[0] = 0xff;
+               data->intdec[1] = 0xff;
+
+               if (data->sgdlist.area) {
+                       snd_dma_free_pages(&data->sgdlist);
+                       data->sgdlist.area = NULL;
+               }
+       }
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_riptide_playback_open(struct snd_pcm_substream *substream)
+{
+       struct snd_riptide *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct pcmhw *data;
+       int index = substream->number;
+
+       chip->playback_substream[index] = substream;
+       runtime->hw = snd_riptide_playback;
+       data = kzalloc(sizeof(struct pcmhw), GFP_KERNEL);
+       data->paths = lbus_play_paths[index];
+       data->id = play_ids[index];
+       data->source = play_sources[index];
+       data->intdec[0] = 0xff;
+       data->intdec[1] = 0xff;
+       data->state = ST_STOP;
+       runtime->private_data = data;
+       return snd_pcm_hw_constraint_integer(runtime,
+                                            SNDRV_PCM_HW_PARAM_PERIODS);
+}
+
+static int snd_riptide_capture_open(struct snd_pcm_substream *substream)
+{
+       struct snd_riptide *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct pcmhw *data;
+
+       chip->capture_substream = substream;
+       runtime->hw = snd_riptide_capture;
+       data = kzalloc(sizeof(struct pcmhw), GFP_KERNEL);
+       data->paths = lbus_rec_path;
+       data->id = PADC;
+       data->source = ACLNK2PADC;
+       data->intdec[0] = 0xff;
+       data->intdec[1] = 0xff;
+       data->state = ST_STOP;
+       runtime->private_data = data;
+       return snd_pcm_hw_constraint_integer(runtime,
+                                            SNDRV_PCM_HW_PARAM_PERIODS);
+}
+
+static int snd_riptide_playback_close(struct snd_pcm_substream *substream)
+{
+       struct snd_riptide *chip = snd_pcm_substream_chip(substream);
+       struct pcmhw *data = get_pcmhwdev(substream);
+       int index = substream->number;
+
+       substream->runtime->private_data = NULL;
+       chip->playback_substream[index] = NULL;
+       kfree(data);
+       return 0;
+}
+
+static int snd_riptide_capture_close(struct snd_pcm_substream *substream)
+{
+       struct snd_riptide *chip = snd_pcm_substream_chip(substream);
+       struct pcmhw *data = get_pcmhwdev(substream);
+
+       substream->runtime->private_data = NULL;
+       chip->capture_substream = NULL;
+       kfree(data);
+       return 0;
+}
+
+static struct snd_pcm_ops snd_riptide_playback_ops = {
+       .open = snd_riptide_playback_open,
+       .close = snd_riptide_playback_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = snd_riptide_hw_params,
+       .hw_free = snd_riptide_hw_free,
+       .prepare = snd_riptide_prepare,
+       .page = snd_pcm_sgbuf_ops_page,
+       .trigger = snd_riptide_trigger,
+       .pointer = snd_riptide_pointer,
+};
+static struct snd_pcm_ops snd_riptide_capture_ops = {
+       .open = snd_riptide_capture_open,
+       .close = snd_riptide_capture_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = snd_riptide_hw_params,
+       .hw_free = snd_riptide_hw_free,
+       .prepare = snd_riptide_prepare,
+       .page = snd_pcm_sgbuf_ops_page,
+       .trigger = snd_riptide_trigger,
+       .pointer = snd_riptide_pointer,
+};
+
+static int __devinit
+snd_riptide_pcm(struct snd_riptide *chip, int device, struct snd_pcm **rpcm)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       if (rpcm)
+               *rpcm = NULL;
+       if ((err =
+            snd_pcm_new(chip->card, "RIPTIDE", device, PLAYBACK_SUBSTREAMS, 1,
+                        &pcm)) < 0)
+               return err;
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                       &snd_riptide_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                       &snd_riptide_capture_ops);
+       pcm->private_data = chip;
+       pcm->info_flags = 0;
+       strcpy(pcm->name, "RIPTIDE");
+       chip->pcm = pcm;
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+                                             snd_dma_pci_data(chip->pci),
+                                             64 * 1024, 128 * 1024);
+       if (rpcm)
+               *rpcm = pcm;
+       return 0;
+}
+
+static irqreturn_t
+snd_riptide_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct snd_riptide *chip = dev_id;
+       struct cmdif *cif = chip->cif;
+
+       if (cif) {
+               chip->received_irqs++;
+               if (IS_EOBIRQ(cif->hwport) || IS_EOSIRQ(cif->hwport) ||
+                   IS_EOCIRQ(cif->hwport)) {
+                       chip->handled_irqs++;
+                       tasklet_hi_schedule(&chip->riptide_tq);
+               }
+               if (chip->rmidi && IS_MPUIRQ(cif->hwport)) {
+                       chip->handled_irqs++;
+                       snd_mpu401_uart_interrupt(irq,
+                                                 chip->rmidi->private_data,
+                                                 regs);
+               }
+               SET_AIACK(cif->hwport);
+       }
+       return IRQ_HANDLED;
+}
+
+static void
+snd_riptide_codec_write(struct snd_ac97 *ac97, unsigned short reg,
+                       unsigned short val)
+{
+       struct snd_riptide *chip = ac97->private_data;
+       struct cmdif *cif = chip->cif;
+       union cmdret rptr = CMDRET_ZERO;
+       int i = 0;
+
+       snd_assert(cif, return);
+
+       snd_printdd("Write AC97 reg 0x%x 0x%x\n", reg, val);
+       do {
+               SEND_SACR(cif, val, reg);
+               SEND_RACR(cif, reg, &rptr);
+       } while (rptr.retwords[1] != val && i++ < MAX_WRITE_RETRY);
+       if (i == MAX_WRITE_RETRY)
+               snd_printdd("Write AC97 reg failed\n");
+}
+
+static unsigned short snd_riptide_codec_read(struct snd_ac97 *ac97,
+                                            unsigned short reg)
+{
+       struct snd_riptide *chip = ac97->private_data;
+       struct cmdif *cif = chip->cif;
+       union cmdret rptr = CMDRET_ZERO;
+
+       snd_assert(cif, return 0);
+
+       if (SEND_RACR(cif, reg, &rptr) != 0)
+               SEND_RACR(cif, reg, &rptr);
+       snd_printdd("Read AC97 reg 0x%x got 0x%x\n", reg, rptr.retwords[1]);
+       return rptr.retwords[1];
+}
+
+static int snd_riptide_initialize(struct snd_riptide *chip)
+{
+       struct cmdif *cif;
+       unsigned int device_id;
+       int err;
+
+       snd_assert(chip, return -EINVAL);
+
+       cif = chip->cif;
+       if (!cif) {
+               if ((cif = kzalloc(sizeof(struct cmdif), GFP_KERNEL)) == NULL)
+                       return -ENOMEM;
+               cif->hwport = (struct riptideport *)chip->port;
+               spin_lock_init(&cif->lock);
+               chip->cif = cif;
+       }
+       cif->is_reset = 0;
+       if ((err = riptide_reset(cif, chip)) != 0)
+               return err;
+       device_id = chip->device_id;
+       switch (device_id) {
+       case 0x4310:
+       case 0x4320:
+       case 0x4330:
+               snd_printdd("Modem enable?\n");
+               SEND_SETDPLL(cif);
+               break;
+       }
+       snd_printdd("Enabling MPU IRQs\n");
+       if (chip->rmidi)
+               SET_EMPUIRQ(cif->hwport);
+       return err;
+}
+
+static int snd_riptide_free(struct snd_riptide *chip)
+{
+       struct cmdif *cif;
+
+       snd_assert(chip, return 0);
+
+       if ((cif = chip->cif)) {
+               SET_GRESET(cif->hwport);
+               udelay(100);
+               UNSET_GRESET(cif->hwport);
+               kfree(chip->cif);
+       }
+       if (chip->fw_entry)
+               release_firmware(chip->fw_entry);
+       release_and_free_resource(chip->res_port);
+       if (chip->irq >= 0)
+               free_irq(chip->irq, chip);
+       kfree(chip);
+       return 0;
+}
+
+static int snd_riptide_dev_free(struct snd_device *device)
+{
+       struct snd_riptide *chip = device->device_data;
+
+       return snd_riptide_free(chip);
+}
+
+static int __devinit
+snd_riptide_create(struct snd_card *card, struct pci_dev *pci,
+                  struct snd_riptide **rchip)
+{
+       struct snd_riptide *chip;
+       struct riptideport *hwport;
+       int err;
+       static struct snd_device_ops ops = {
+               .dev_free = snd_riptide_dev_free,
+       };
+
+       *rchip = NULL;
+       if ((err = pci_enable_device(pci)) < 0)
+               return err;
+       if (!(chip = kzalloc(sizeof(struct snd_riptide), GFP_KERNEL)))
+               return -ENOMEM;
+
+       spin_lock_init(&chip->lock);
+       chip->card = card;
+       chip->pci = pci;
+       chip->irq = -1;
+       chip->openstreams = 0;
+       chip->port = pci_resource_start(pci, 0);
+       chip->received_irqs = 0;
+       chip->handled_irqs = 0;
+       chip->cif = NULL;
+       tasklet_init(&chip->riptide_tq, riptide_handleirq, (unsigned long)chip);
+
+       if ((chip->res_port =
+            request_region(chip->port, 64, "RIPTIDE")) == NULL) {
+               snd_printk(KERN_ERR
+                          "Riptide: unable to grab region 0x%lx-0x%lx\n",
+                          chip->port, chip->port + 64 - 1);
+               snd_riptide_free(chip);
+               return -EBUSY;
+       }
+       hwport = (struct riptideport *)chip->port;
+       UNSET_AIE(hwport);
+
+       if (request_irq
+           (pci->irq, snd_riptide_interrupt, SA_INTERRUPT | SA_SHIRQ,
+            "RIPTIDE", chip)) {
+               snd_printk(KERN_ERR "Riptide: unable to grab IRQ %d\n",
+                          pci->irq);
+               snd_riptide_free(chip);
+               return -EBUSY;
+       }
+       chip->irq = pci->irq;
+       chip->device_id = pci->device;
+       pci_set_master(pci);
+       if ((err = snd_riptide_initialize(chip)) < 0) {
+               snd_riptide_free(chip);
+               return err;
+       }
+
+       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+               snd_riptide_free(chip);
+               return err;
+       }
+
+       *rchip = chip;
+       return 0;
+}
+
+static void
+snd_riptide_proc_read(struct snd_info_entry *entry,
+                     struct snd_info_buffer *buffer)
+{
+       struct snd_riptide *chip = entry->private_data;
+       struct pcmhw *data;
+       int i;
+       struct cmdif *cif = NULL;
+       unsigned char p[256];
+       unsigned short rval = 0, lval = 0;
+       unsigned int rate;
+
+       if (!chip)
+               return;
+
+       snd_iprintf(buffer, "%s\n\n", chip->card->longname);
+       snd_iprintf(buffer, "Device ID: 0x%x\nReceived IRQs: (%ld)%ld\nPorts:",
+                   chip->device_id, chip->handled_irqs, chip->received_irqs);
+       for (i = 0; i < 64; i += 4)
+               snd_iprintf(buffer, "%c%02x: %08x",
+                           (i % 16) ? ' ' : '\n', i, inl(chip->port + i));
+       if ((cif = chip->cif)) {
+               snd_iprintf(buffer,
+                           "\nVersion: ASIC: %d CODEC: %d AUXDSP: %d PROG: %d",
+                           chip->firmware.firmware.ASIC,
+                           chip->firmware.firmware.CODEC,
+                           chip->firmware.firmware.AUXDSP,
+                           chip->firmware.firmware.PROG);
+               snd_iprintf(buffer, "\nDigital mixer:");
+               for (i = 0; i < 12; i++) {
+                       getmixer(cif, i, &rval, &lval);
+                       snd_iprintf(buffer, "\n %d: %d %d", i, rval, lval);
+               }
+               snd_iprintf(buffer,
+                           "\nARM Commands num: %d failed: %d time: %d max: %d min: %d",
+                           cif->cmdcnt, cif->errcnt,
+                           cif->cmdtime, cif->cmdtimemax, cif->cmdtimemin);
+       }
+       snd_iprintf(buffer, "\nOpen streams %d:\n", chip->openstreams);
+       for (i = 0; i < PLAYBACK_SUBSTREAMS; i++) {
+               if (chip->playback_substream[i]
+                   && chip->playback_substream[i]->runtime
+                   && (data =
+                       chip->playback_substream[i]->runtime->private_data)) {
+                       snd_iprintf(buffer,
+                                   "stream: %d mixer: %d source: %d (%d,%d)\n",
+                                   data->id, data->mixer, data->source,
+                                   data->intdec[0], data->intdec[1]);
+                       if (!(getsamplerate(cif, data->intdec, &rate)))
+                               snd_iprintf(buffer, "rate: %d\n", rate);
+               }
+       }
+       if (chip->capture_substream
+           && chip->capture_substream->runtime
+           && (data = chip->capture_substream->runtime->private_data)) {
+               snd_iprintf(buffer,
+                           "stream: %d mixer: %d source: %d (%d,%d)\n",
+                           data->id, data->mixer,
+                           data->source, data->intdec[0], data->intdec[1]);
+               if (!(getsamplerate(cif, data->intdec, &rate)))
+                       snd_iprintf(buffer, "rate: %d\n", rate);
+       }
+       snd_iprintf(buffer, "Paths:\n");
+       i = getpaths(cif, p);
+       while (i--) {
+               snd_iprintf(buffer, "%x->%x ", p[i - 1], p[i]);
+               i--;
+       }
+       snd_iprintf(buffer, "\n");
+}
+
+static void __devinit snd_riptide_proc_init(struct snd_riptide *chip)
+{
+       struct snd_info_entry *entry;
+
+       if (!snd_card_proc_new(chip->card, "riptide", &entry))
+               snd_info_set_text_ops(entry, chip, 4096, snd_riptide_proc_read);
+}
+
+static int __devinit snd_riptide_mixer(struct snd_riptide *chip)
+{
+       struct snd_ac97_bus *pbus;
+       struct snd_ac97_template ac97;
+       int err = 0;
+       static struct snd_ac97_bus_ops ops = {
+               .write = snd_riptide_codec_write,
+               .read = snd_riptide_codec_read,
+       };
+
+       memset(&ac97, 0, sizeof(ac97));
+       ac97.private_data = chip;
+       ac97.scaps = AC97_SCAP_SKIP_MODEM;
+
+       if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+               return err;
+
+       chip->ac97_bus = pbus;
+       ac97.pci = chip->pci;
+       if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
+               return err;
+       return err;
+}
+
+#ifdef SUPPORT_JOYSTICK
+static int have_joystick;
+static struct pci_dev *riptide_gameport_pci;
+static struct gameport *riptide_gameport;
+
+static int __devinit
+snd_riptide_joystick_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+       static int dev;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       if (joystick_port[dev]) {
+               riptide_gameport = gameport_allocate_port();
+               if (riptide_gameport) {
+                       if (!request_region
+                           (joystick_port[dev], 8, "Riptide gameport")) {
+                               snd_printk(KERN_WARNING
+                                          "Riptide: cannot grab gameport 0x%x\n",
+                                          joystick_port[dev]);
+                               gameport_free_port(riptide_gameport);
+                               riptide_gameport = NULL;
+                       } else {
+                               riptide_gameport_pci = pci;
+                               riptide_gameport->io = joystick_port[dev];
+                               gameport_register_port(riptide_gameport);
+                       }
+               }
+       }
+       dev++;
+       return 0;
+}
+
+static void __devexit snd_riptide_joystick_remove(struct pci_dev *pci)
+{
+       if (riptide_gameport) {
+               if (riptide_gameport_pci == pci) {
+                       release_region(riptide_gameport->io, 8);
+                       riptide_gameport_pci = NULL;
+                       gameport_unregister_port(riptide_gameport);
+                       riptide_gameport = NULL;
+               }
+       }
+}
+#endif
+
+static int __devinit
+snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+       static int dev;
+       struct snd_card *card;
+       struct snd_riptide *chip;
+       unsigned short addr;
+       int err = 0;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+       if (card == NULL)
+               return -ENOMEM;
+       if ((err = snd_riptide_create(card, pci, &chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       card->private_data = chip;
+       if ((err = snd_riptide_pcm(chip, 0, NULL)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       if ((err = snd_riptide_mixer(chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       pci_write_config_word(chip->pci, PCI_EXT_Legacy_Mask, LEGACY_ENABLE_ALL
+                             | (opl3_port[dev] ? LEGACY_ENABLE_FM : 0)
+#ifdef SUPPORT_JOYSTICK
+                             | (joystick_port[dev] ? LEGACY_ENABLE_GAMEPORT :
+                                0)
+#endif
+                             | (mpu_port[dev]
+                                ? (LEGACY_ENABLE_MPU_INT | LEGACY_ENABLE_MPU) :
+                                0)
+                             | ((chip->irq << 4) & 0xF0));
+       if ((addr = mpu_port[dev]) != 0) {
+               pci_write_config_word(chip->pci, PCI_EXT_MPU_Base, addr);
+               if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_RIPTIDE,
+                                              addr, 0, chip->irq, 0,
+                                              &chip->rmidi)) < 0)
+                       snd_printk(KERN_WARNING
+                                  "Riptide: Can't Allocate MPU at 0x%x\n",
+                                  addr);
+               else
+                       chip->mpuaddr = addr;
+       }
+       if ((addr = opl3_port[dev]) != 0) {
+               pci_write_config_word(chip->pci, PCI_EXT_FM_Base, addr);
+               if ((err = snd_opl3_create(card, addr, addr + 2,
+                                          OPL3_HW_RIPTIDE, 0,
+                                          &chip->opl3)) < 0)
+                       snd_printk(KERN_WARNING
+                                  "Riptide: Can't Allocate OPL3 at 0x%x\n",
+                                  addr);
+               else {
+                       chip->opladdr = addr;
+                       if ((err =
+                            snd_opl3_hwdep_new(chip->opl3, 0, 1, NULL)) < 0)
+                               snd_printk(KERN_WARNING
+                                          "Riptide: Can't Allocate OPL3-HWDEP\n");
+               }
+       }
+#ifdef SUPPORT_JOYSTICK
+       if ((addr = joystick_port[dev]) != 0) {
+               pci_write_config_word(chip->pci, PCI_EXT_Game_Base, addr);
+               chip->gameaddr = addr;
+       }
+#endif
+
+       strcpy(card->driver, "RIPTIDE");
+       strcpy(card->shortname, "Riptide");
+#ifdef SUPPORT_JOYSTICK
+       snprintf(card->longname, sizeof(card->longname),
+                "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x gameport 0x%x",
+                card->shortname, chip->port, chip->irq, chip->mpuaddr,
+                chip->opladdr, chip->gameaddr);
+#else
+       snprintf(card->longname, sizeof(card->longname),
+                "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x",
+                card->shortname, chip->port, chip->irq, chip->mpuaddr,
+                chip->opladdr);
+#endif
+       snd_riptide_proc_init(chip);
+       if ((err = snd_card_register(card)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       pci_set_drvdata(pci, card);
+       dev++;
+       return 0;
+}
+
+static void __devexit snd_card_riptide_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+       .name = "RIPTIDE",
+       .id_table = snd_riptide_ids,
+       .probe = snd_card_riptide_probe,
+       .remove = __devexit_p(snd_card_riptide_remove),
+#ifdef CONFIG_PM
+       .suspend = riptide_suspend,
+       .resume = riptide_resume,
+#endif
+};
+
+#ifdef SUPPORT_JOYSTICK
+static struct pci_driver joystick_driver = {
+       .name = "Riptide Joystick",
+       .id_table = snd_riptide_joystick_ids,
+       .probe = snd_riptide_joystick_probe,
+       .remove = __devexit_p(snd_riptide_joystick_remove),
+};
+#endif
+
+static int __init alsa_card_riptide_init(void)
+{
+       int err;
+       if ((err = pci_register_driver(&driver)) < 0)
+               return err;
+#if defined(SUPPORT_JOYSTICK)
+       if (pci_register_driver(&joystick_driver) < 0) {
+               have_joystick = 0;
+               snd_printk(KERN_INFO "no joystick found\n");
+       } else
+               have_joystick = 1;
+#endif
+       return 0;
+}
+
+static void __exit alsa_card_riptide_exit(void)
+{
+       pci_unregister_driver(&driver);
+#if defined(SUPPORT_JOYSTICK)
+       if (have_joystick)
+               pci_unregister_driver(&joystick_driver);
+#endif
+}
+
+module_init(alsa_card_riptide_init);
+module_exit(alsa_card_riptide_exit);
index 1957d29..1e7398d 100644 (file)
@@ -2373,6 +2373,7 @@ static int __devinit check_dxs_list(struct pci_dev *pci)
                { .subvendor = 0x161f, .subdevice = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */
                { .subvendor = 0x1631, .subdevice = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */
                { .subvendor = 0x1695, .subdevice = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */
+               { .subvendor = 0x1695, .subdevice = 0x300c, .action = VIA_DXS_SRC }, /* EPoX EP-8KRAI */
                { .subvendor = 0x1695, .subdevice = 0x300e, .action = VIA_DXS_SRC }, /* EPoX 9HEAI */
                { .subvendor = 0x16f3, .subdevice = 0x6405, .action = VIA_DXS_SRC }, /* Jetway K8M8MS */
                { .subvendor = 0x1734, .subdevice = 0x1078, .action = VIA_DXS_SRC }, /* FSC Amilo L7300 */
index 77caf43..adfdce7 100644 (file)
@@ -57,18 +57,12 @@ static struct snd_card *card_list[SNDRV_CARDS];
 /*
  * prototypes
  */
-static void pdacf_config(dev_link_t *link);
+static int pdacf_config(struct pcmcia_device *link);
 static void snd_pdacf_detach(struct pcmcia_device *p_dev);
 
-static void pdacf_release(dev_link_t *link)
+static void pdacf_release(struct pcmcia_device *link)
 {
-       if (link->state & DEV_CONFIG) {
-               /* release cs resources */
-               pcmcia_release_configuration(link->handle);
-               pcmcia_release_io(link->handle, &link->io);
-               pcmcia_release_irq(link->handle, &link->irq);
-               link->state &= ~DEV_CONFIG;
-       }
+       pcmcia_disable_device(link);
 }
 
 /*
@@ -76,7 +70,7 @@ static void pdacf_release(dev_link_t *link)
  */
 static int snd_pdacf_free(struct snd_pdacf *pdacf)
 {
-       dev_link_t *link = &pdacf->link;
+       struct pcmcia_device *link = pdacf->p_dev;
 
        pdacf_release(link);
 
@@ -96,10 +90,9 @@ static int snd_pdacf_dev_free(struct snd_device *device)
 /*
  * snd_pdacf_attach - attach callback for cs
  */
-static int snd_pdacf_attach(struct pcmcia_device *p_dev)
+static int snd_pdacf_probe(struct pcmcia_device *link)
 {
        int i;
-       dev_link_t *link;               /* Info for cardmgr */
        struct snd_pdacf *pdacf;
        struct snd_card *card;
        static struct snd_device_ops ops = {
@@ -139,7 +132,7 @@ static int snd_pdacf_attach(struct pcmcia_device *p_dev)
        pdacf->index = i;
        card_list[i] = card;
 
-       link = &pdacf->link;
+       pdacf->p_dev = link;
        link->priv = pdacf;
 
        link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
@@ -156,13 +149,7 @@ static int snd_pdacf_attach(struct pcmcia_device *p_dev)
        link->conf.ConfigIndex = 1;
        link->conf.Present = PRESENT_OPTION;
 
-       /* Chain drivers */
-       link->next = NULL;
-
-       link->handle = p_dev;
-       pdacf_config(link);
-
-       return 0;
+       return pdacf_config(link);
 }
 
 
@@ -209,9 +196,8 @@ static int snd_pdacf_assign_resources(struct snd_pdacf *pdacf, int port, int irq
 /*
  * snd_pdacf_detach - detach callback for cs
  */
-static void snd_pdacf_detach(struct pcmcia_device *p_dev)
+static void snd_pdacf_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct snd_pdacf *chip = link->priv;
 
        snd_printdd(KERN_DEBUG "pdacf_detach called\n");
@@ -230,13 +216,11 @@ static void snd_pdacf_detach(struct pcmcia_device *p_dev)
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void pdacf_config(dev_link_t *link)
+static int pdacf_config(struct pcmcia_device *link)
 {
-       client_handle_t handle = link->handle;
        struct snd_pdacf *pdacf = link->priv;
        tuple_t tuple;
        cisparse_t *parse = NULL;
-       config_info_t conf;
        u_short buf[32];
        int last_fn, last_ret;
 
@@ -244,7 +228,7 @@ static void pdacf_config(dev_link_t *link)
        parse = kmalloc(sizeof(*parse), GFP_KERNEL);
        if (! parse) {
                snd_printk(KERN_ERR "pdacf_config: cannot allocate\n");
-               return;
+               return -ENOMEM;
        }
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
        tuple.Attributes = 0;
@@ -252,71 +236,51 @@ static void pdacf_config(dev_link_t *link)
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
        tuple.DesiredTuple = CISTPL_CONFIG;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, parse));
        link->conf.ConfigBase = parse->config.base;
        link->conf.ConfigIndex = 0x5;
        kfree(parse);
 
-       CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
-       link->conf.Vcc = conf.Vcc;
-
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
-       CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io));
-       CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+       CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
+       CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
        if (snd_pdacf_assign_resources(pdacf, link->io.BasePort1, link->irq.AssignedIRQ) < 0)
                goto failed;
 
-       link->dev = &pdacf->node;
-       link->state &= ~DEV_CONFIG_PENDING;
-       return;
+       link->dev_node = &pdacf->node;
+       return 0;
 
 cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 failed:
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
+       pcmcia_disable_device(link);
+       return -ENODEV;
 }
 
 #ifdef CONFIG_PM
 
-static int pdacf_suspend(struct pcmcia_device *dev)
+static int pdacf_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        struct snd_pdacf *chip = link->priv;
 
        snd_printdd(KERN_DEBUG "SUSPEND\n");
-       link->state |= DEV_SUSPEND;
        if (chip) {
                snd_printdd(KERN_DEBUG "snd_pdacf_suspend calling\n");
                snd_pdacf_suspend(chip, PMSG_SUSPEND);
        }
 
-       snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n");
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
-
        return 0;
 }
 
-static int pdacf_resume(struct pcmcia_device *dev)
+static int pdacf_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        struct snd_pdacf *chip = link->priv;
 
        snd_printdd(KERN_DEBUG "RESUME\n");
-       link->state &= ~DEV_SUSPEND;
-
-       snd_printdd(KERN_DEBUG "CARD_RESET\n");
-       if (DEV_OK(link)) {
-               snd_printdd(KERN_DEBUG "requestconfig...\n");
-               pcmcia_request_configuration(link->handle, &link->conf);
+       if (pcmcia_dev_present(link)) {
                if (chip) {
                        snd_printdd(KERN_DEBUG "calling snd_pdacf_resume\n");
                        snd_pdacf_resume(chip);
@@ -343,7 +307,7 @@ static struct pcmcia_driver pdacf_cs_driver = {
        .drv            = {
                .name   = "snd-pdaudiocf",
        },
-       .probe          = snd_pdacf_attach,
+       .probe          = snd_pdacf_probe,
        .remove         = snd_pdacf_detach,
        .id_table       = snd_pdacf_ids,
 #ifdef CONFIG_PM
index 2744f18..9a14a4f 100644 (file)
@@ -116,7 +116,7 @@ struct snd_pdacf {
        void *pcm_area;
        
        /* pcmcia stuff */
-       dev_link_t link;
+       struct pcmcia_device    *p_dev;
        dev_node_t node;
 };
 
index 66900d2..7e0cda2 100644 (file)
@@ -59,15 +59,9 @@ static unsigned int card_alloc;
 
 /*
  */
-static void vxpocket_release(dev_link_t *link)
+static void vxpocket_release(struct pcmcia_device *link)
 {
-       if (link->state & DEV_CONFIG) {
-               /* release cs resources */
-               pcmcia_release_configuration(link->handle);
-               pcmcia_release_io(link->handle, &link->io);
-               pcmcia_release_irq(link->handle, &link->irq);
-               link->state &= ~DEV_CONFIG;
-       }
+       pcmcia_disable_device(link);
 }
 
 /*
@@ -132,9 +126,9 @@ static struct snd_vx_hardware vxp440_hw = {
 /*
  * create vxpocket instance
  */
-static struct snd_vxpocket *snd_vxpocket_new(struct snd_card *card, int ibl)
+static struct snd_vxpocket *snd_vxpocket_new(struct snd_card *card, int ibl,
+                                            struct pcmcia_device *link)
 {
-       dev_link_t *link;               /* Info for cardmgr */
        struct vx_core *chip;
        struct snd_vxpocket *vxp;
        static struct snd_device_ops ops = {
@@ -154,7 +148,7 @@ static struct snd_vxpocket *snd_vxpocket_new(struct snd_card *card, int ibl)
 
        vxp = (struct snd_vxpocket *)chip;
 
-       link = &vxp->link;
+       vxp->p_dev = link;
        link->priv = chip;
 
        link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
@@ -167,7 +161,6 @@ static struct snd_vxpocket *snd_vxpocket_new(struct snd_card *card, int ibl)
        link->irq.Instance = chip;
 
        link->conf.Attributes = CONF_ENABLE_IRQ;
-       link->conf.Vcc = 50;
        link->conf.IntType = INT_MEMORY_AND_IO;
        link->conf.ConfigIndex = 1;
        link->conf.Present = PRESENT_OPTION;
@@ -215,9 +208,8 @@ static int snd_vxpocket_assign_resources(struct vx_core *chip, int port, int irq
 #define CS_CHECK(fn, ret) \
 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
-static void vxpocket_config(dev_link_t *link)
+static int vxpocket_config(struct pcmcia_device *link)
 {
-       client_handle_t handle = link->handle;
        struct vx_core *chip = link->priv;
        struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
        tuple_t tuple;
@@ -229,24 +221,24 @@ static void vxpocket_config(dev_link_t *link)
        parse = kmalloc(sizeof(*parse), GFP_KERNEL);
        if (! parse) {
                snd_printk(KERN_ERR "vx: cannot allocate\n");
-               return;
+               return -ENOMEM;
        }
        tuple.Attributes = 0;
        tuple.TupleData = (cisdata_t *)buf;
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
        tuple.DesiredTuple = CISTPL_CONFIG;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, parse));
        link->conf.ConfigBase = parse->config.base;
        link->conf.Present = parse->config.rmask[0];
 
        /* redefine hardware record according to the VERSION1 string */
        tuple.DesiredTuple = CISTPL_VERS_1;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, parse));
        if (! strcmp(parse->version_1.str + parse->version_1.ofs[1], "VX-POCKET")) {
                snd_printdd("VX-pocket is detected\n");
        } else {
@@ -257,67 +249,50 @@ static void vxpocket_config(dev_link_t *link)
                strcpy(chip->card->driver, vxp440_hw.name);
        }
 
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-
-       CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io));
-       CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+       CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
+       CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
 
-       chip->dev = &handle_to_dev(link->handle);
+       chip->dev = &handle_to_dev(link);
        snd_card_set_dev(chip->card, chip->dev);
 
        if (snd_vxpocket_assign_resources(chip, link->io.BasePort1, link->irq.AssignedIRQ) < 0)
                goto failed;
 
-       link->dev = &vxp->node;
-       link->state &= ~DEV_CONFIG_PENDING;
+       link->dev_node = &vxp->node;
        kfree(parse);
-       return;
+       return 9;
 
 cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
 failed:
-       pcmcia_release_configuration(link->handle);
-       pcmcia_release_io(link->handle, &link->io);
-       pcmcia_release_irq(link->handle, &link->irq);
-       link->state &= ~DEV_CONFIG;
+       pcmcia_disable_device(link);
        kfree(parse);
+       return -ENODEV;
 }
 
 #ifdef CONFIG_PM
 
-static int vxp_suspend(struct pcmcia_device *dev)
+static int vxp_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        struct vx_core *chip = link->priv;
 
        snd_printdd(KERN_DEBUG "SUSPEND\n");
-       link->state |= DEV_SUSPEND;
        if (chip) {
                snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n");
                snd_vx_suspend(chip, PMSG_SUSPEND);
        }
-       snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n");
-       if (link->state & DEV_CONFIG)
-               pcmcia_release_configuration(link->handle);
 
        return 0;
 }
 
-static int vxp_resume(struct pcmcia_device *dev)
+static int vxp_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(dev);
        struct vx_core *chip = link->priv;
 
        snd_printdd(KERN_DEBUG "RESUME\n");
-       link->state &= ~DEV_SUSPEND;
-
-       snd_printdd(KERN_DEBUG "CARD_RESET\n");
-       if (DEV_OK(link)) {
+       if (pcmcia_dev_present(link)) {
                //struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
-               snd_printdd(KERN_DEBUG "requestconfig...\n");
-               pcmcia_request_configuration(link->handle, &link->conf);
                if (chip) {
                        snd_printdd(KERN_DEBUG "calling snd_vx_resume\n");
                        snd_vx_resume(chip);
@@ -333,7 +308,7 @@ static int vxp_resume(struct pcmcia_device *dev)
 
 /*
  */
-static int vxpocket_attach(struct pcmcia_device *p_dev)
+static int vxpocket_probe(struct pcmcia_device *p_dev)
 {
        struct snd_card *card;
        struct snd_vxpocket *vxp;
@@ -358,7 +333,7 @@ static int vxpocket_attach(struct pcmcia_device *p_dev)
                return -ENOMEM;
        }
 
-       vxp = snd_vxpocket_new(card, ibl[i]);
+       vxp = snd_vxpocket_new(card, ibl[i], p_dev);
        if (! vxp) {
                snd_card_free(card);
                return -ENODEV;
@@ -368,20 +343,13 @@ static int vxpocket_attach(struct pcmcia_device *p_dev)
        vxp->index = i;
        card_alloc |= 1 << i;
 
-       /* Chain drivers */
-       vxp->link.next = NULL;
-
-       vxp->link.handle = p_dev;
-       vxp->link.state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       p_dev->instance = &vxp->link;
-       vxpocket_config(&vxp->link);
+       vxp->p_dev = p_dev;
 
-       return 0;
+       return vxpocket_config(p_dev);
 }
 
-static void vxpocket_detach(struct pcmcia_device *p_dev)
+static void vxpocket_detach(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        struct snd_vxpocket *vxp;
        struct vx_core *chip;
 
@@ -413,7 +381,7 @@ static struct pcmcia_driver vxp_cs_driver = {
        .drv            = {
                .name   = "snd-vxpocket",
        },
-       .probe          = vxpocket_attach,
+       .probe          = vxpocket_probe,
        .remove         = vxpocket_detach,
        .id_table       = vxp_ids,
 #ifdef CONFIG_PM
index 67efae3..27ea002 100644 (file)
@@ -42,7 +42,7 @@ struct snd_vxpocket {
        int index;      /* card index */
 
        /* pcmcia stuff */
-       dev_link_t link;
+       struct pcmcia_device    *p_dev;
        dev_node_t node;
 };
 
index 8d08b34..ce86283 100644 (file)
@@ -306,8 +306,8 @@ static int get_relative_value(struct usb_mixer_elem_info *cval, int val)
                cval->res = 1;
        if (val < cval->min)
                return 0;
-       else if (val > cval->max)
-               return (cval->max - cval->min) / cval->res;
+       else if (val >= cval->max)
+               return (cval->max - cval->min + cval->res - 1) / cval->res;
        else
                return (val - cval->min) / cval->res;
 }
@@ -670,6 +670,36 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
                }
                if (cval->res == 0)
                        cval->res = 1;
+
+               /* Additional checks for the proper resolution
+                *
+                * Some devices report smaller resolutions than actually
+                * reacting.  They don't return errors but simply clip
+                * to the lower aligned value.
+                */
+               if (cval->min + cval->res < cval->max) {
+                       int last_valid_res = cval->res;
+                       int saved, test, check;
+                       get_cur_mix_value(cval, minchn, &saved);
+                       for (;;) {
+                               test = saved;
+                               if (test < cval->max)
+                                       test += cval->res;
+                               else
+                                       test -= cval->res;
+                               if (test < cval->min || test > cval->max ||
+                                   set_cur_mix_value(cval, minchn, test) ||
+                                   get_cur_mix_value(cval, minchn, &check)) {
+                                       cval->res = last_valid_res;
+                                       break;
+                               }
+                               if (test == check)
+                                       break;
+                               cval->res *= 2;
+                       }
+                       set_cur_mix_value(cval, minchn, saved);
+               }
+
                cval->initialized = 1;
        }
        return 0;
@@ -695,7 +725,8 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
                if (! cval->initialized)
                        get_min_max(cval,  0);
                uinfo->value.integer.min = 0;
-               uinfo->value.integer.max = (cval->max - cval->min) / cval->res;
+               uinfo->value.integer.max =
+                       (cval->max - cval->min + cval->res - 1) / cval->res;
        }
        return 0;
 }