aboutsummaryrefslogtreecommitdiffstats
path: root/include/uapi/linux
Commit message (Collapse)AuthorAge
* Introduce main LITMUS^RT headerBjoern Brandenburg2017-05-26
| | | | | This patch adds a basic litmus/litmus.h, which is required for basic LITMUS^RT infrastructure to compile.
* net: ipv6: RTF_PCPU should not be settable from userspaceDavid Ahern2017-05-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | [ Upstream commit 557c44be917c322860665be3d28376afa84aa936 ] Andrey reported a fault in the IPv6 route code: kasan: GPF could be caused by NULL-ptr deref or user memory access general protection fault: 0000 [#1] SMP KASAN Modules linked in: CPU: 1 PID: 4035 Comm: a.out Not tainted 4.11.0-rc7+ #250 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 task: ffff880069809600 task.stack: ffff880062dc8000 RIP: 0010:ip6_rt_cache_alloc+0xa6/0x560 net/ipv6/route.c:975 RSP: 0018:ffff880062dced30 EFLAGS: 00010206 RAX: dffffc0000000000 RBX: ffff8800670561c0 RCX: 0000000000000006 RDX: 0000000000000003 RSI: ffff880062dcfb28 RDI: 0000000000000018 RBP: ffff880062dced68 R08: 0000000000000001 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: ffff880062dcfb28 R14: dffffc0000000000 R15: 0000000000000000 FS: 00007feebe37e7c0(0000) GS:ffff88006cb00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000205a0fe4 CR3: 000000006b5c9000 CR4: 00000000000006e0 Call Trace: ip6_pol_route+0x1512/0x1f20 net/ipv6/route.c:1128 ip6_pol_route_output+0x4c/0x60 net/ipv6/route.c:1212 ... Andrey's syzkaller program passes rtmsg.rtmsg_flags with the RTF_PCPU bit set. Flags passed to the kernel are blindly copied to the allocated rt6_info by ip6_route_info_create making a newly inserted route appear as though it is a per-cpu route. ip6_rt_cache_alloc sees the flag set and expects rt->dst.from to be set - which it is not since it is not really a per-cpu copy. The subsequent call to __ip6_dst_alloc then generates the fault. Fix by checking for the flag and failing with EINVAL. Fixes: d52d3997f843f ("ipv6: Create percpu rt6_info") Reported-by: Andrey Konovalov <andreyknvl@google.com> Signed-off-by: David Ahern <dsa@cumulusnetworks.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Tested-by: Andrey Konovalov <andreyknvl@google.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
* uapi: fix linux/packet_diag.h userspace compilation errorDmitry V. Levin2017-03-22
| | | | | | | | | | | | | | | | | | | | | | | | [ Upstream commit 745cb7f8a5de0805cade3de3991b7a95317c7c73 ] Replace MAX_ADDR_LEN with its numeric value to fix the following linux/packet_diag.h userspace compilation error: /usr/include/linux/packet_diag.h:67:17: error: 'MAX_ADDR_LEN' undeclared here (not in a function) __u8 pdmc_addr[MAX_ADDR_LEN]; This is not the first case in the UAPI where the numeric value of MAX_ADDR_LEN is used instead of symbolic one, uapi/linux/if_link.h already does the same: $ grep MAX_ADDR_LEN include/uapi/linux/if_link.h __u8 mac[32]; /* MAX_ADDR_LEN */ There are no UAPI headers besides these two that use MAX_ADDR_LEN. Signed-off-by: Dmitry V. Levin <ldv@altlinux.org> Acked-by: Pavel Emelyanov <xemul@virtuozzo.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
* fs: Better permission checking for submountsEric W. Biederman2017-03-14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | commit 93faccbbfa958a9668d3ab4e30f38dd205cee8d8 upstream. To support unprivileged users mounting filesystems two permission checks have to be performed: a test to see if the user allowed to create a mount in the mount namespace, and a test to see if the user is allowed to access the specified filesystem. The automount case is special in that mounting the original filesystem grants permission to mount the sub-filesystems, to any user who happens to stumble across the their mountpoint and satisfies the ordinary filesystem permission checks. Attempting to handle the automount case by using override_creds almost works. It preserves the idea that permission to mount the original filesystem is permission to mount the sub-filesystem. Unfortunately using override_creds messes up the filesystems ordinary permission checks. Solve this by being explicit that a mount is a submount by introducing vfs_submount, and using it where appropriate. vfs_submount uses a new mount internal mount flags MS_SUBMOUNT, to let sget and friends know that a mount is a submount so they can take appropriate action. sget and sget_userns are modified to not perform any permission checks on submounts. follow_automount is modified to stop using override_creds as that has proven problemantic. do_mount is modified to always remove the new MS_SUBMOUNT flag so that we know userspace will never by able to specify it. autofs4 is modified to stop using current_real_cred that was put in there to handle the previous version of submount permission checking. cifs is modified to pass the mountpoint all of the way down to vfs_submount. debugfs is modified to pass the mountpoint all of the way down to trace_automount by adding a new parameter. To make this change easier a new typedef debugfs_automount_t is introduced to capture the type of the debugfs automount function. Fixes: 069d5ac9ae0d ("autofs: Fix automounts by using current_real_cred()->uid") Fixes: aeaa4a79ff6a ("fs: Call d_automount with the filesystems creds") Reviewed-by: Trond Myklebust <trond.myklebust@primarydata.com> Reviewed-by: Seth Forshee <seth.forshee@canonical.com> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
* videodev2.h: go back to limited range Y'CbCr for SRGB and, ADOBERGBHans Verkuil2017-02-23
| | | | | | | | | | | | | | | | | | | | | | | | commit 35879ee4769099905fa3bda0b21e73d434e2df6a upstream. This reverts 'commit 7e0739cd9c40 ("[media] videodev2.h: fix sYCC/AdobeYCC default quantization range"). The problem is that many drivers can convert R'G'B' content (often from sensors) to Y'CbCr, but they all produce limited range Y'CbCr. To stay backwards compatible the default quantization range for sRGB and AdobeRGB Y'CbCr encoding should be limited range, not full range, even though the corresponding standards specify full range. Update the V4L2_MAP_QUANTIZATION_DEFAULT define accordingly and also update the documentation. Fixes: 7e0739cd9c40 ("[media] videodev2.h: fix sYCC/AdobeYCC default quantization range") Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
* nl80211: Use different attrs for BSSID and random MAC addr in scan reqVamsi Krishna2017-01-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | commit 2fa436b3a2a7009c11a3bc03fe0ff4c26e80fd87 upstream. NL80211_ATTR_MAC was used to set both the specific BSSID to be scanned and the random MAC address to be used when privacy is enabled. When both the features are enabled, both the BSSID and the local MAC address were getting same value causing Probe Request frames to go with unintended DA. Hence, this has been fixed by using a different NL80211_ATTR_BSSID attribute to set the specific BSSID (which was the more recent addition in cfg80211) for a scan. Backwards compatibility with old userspace software is maintained to some extent by allowing NL80211_ATTR_MAC to be used to set the specific BSSID when scanning without enabling random MAC address use. Scanning with random source MAC address was introduced by commit ad2b26abc157 ("cfg80211: allow drivers to support random MAC addresses for scan") and the issue was introduced with the addition of the second user for the same attribute in commit 818965d39177 ("cfg80211: Allow a scan request for a specific BSSID"). Fixes: 818965d39177 ("cfg80211: Allow a scan request for a specific BSSID") Signed-off-by: Vamsi Krishna <vamsin@qti.qualcomm.com> Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
* can: raw: raw_setsockopt: limit number of can_filter that can be setMarc Kleine-Budde2016-12-07
| | | | | | | | | | | | | This patch adds a check to limit the number of can_filters that can be set via setsockopt on CAN_RAW sockets. Otherwise allocations > MAX_ORDER are not prevented resulting in a warning. Reference: https://lkml.org/lkml/2016/12/2/230 Reported-by: Andrey Konovalov <andreyknvl@google.com> Tested-by: Andrey Konovalov <andreyknvl@google.com> Cc: linux-stable <stable@vger.kernel.org> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
* uapi glibc compat: fix outer guard of net device flags enumJonas Gorski2016-12-06
| | | | | | | | | | | | | | | | | Fix a wrong condition preventing the higher net device flags IFF_LOWER_UP etc to be defined if net/if.h is included before linux/if.h. The comment makes it clear the intention was to allow partial definition with either parts. This fixes compilation of userspace programs trying to use IFF_LOWER_UP, IFF_DORMANT or IFF_ECHO. Fixes: 4a91cb61bb99 ("uapi glibc compat: fix compile errors when glibc net/if.h included before linux/if.h") Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com> Reviewed-by: Mikko Rapeli <mikko.rapeli@iki.fi> Signed-off-by: David S. Miller <davem@davemloft.net>
* uapi: export nf_log.hstephen hemminger2016-12-05
| | | | | | | | | | | File is in uapi directory but not being copied on make install_headers Fixes commit 4ec9c8fbbc22 ("netfilter: nft_log: complete NFTA_LOG_FLAGS attr support"). Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* uapi: export tc_skbmod.hstephen hemminger2016-12-05
| | | | | | | | Fixes commit 735cffe5d800 ("net_sched: Introduce skbmod action") Not used by iproute2 but maybe in future. Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/netLinus Torvalds2016-12-02
|\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Pull networking fixes from David Miller: 1) Lots more phydev and probe error path leaks in various drivers by Johan Hovold. 2) Fix race in packet_set_ring(), from Philip Pettersson. 3) Use after free in dccp_invalid_packet(), from Eric Dumazet. 4) Signnedness overflow in SO_{SND,RCV}BUFFORCE, also from Eric Dumazet. 5) When tunneling between ipv4 and ipv6 we can be left with the wrong skb->protocol value as we enter the IPSEC engine and this causes all kinds of problems. Set it before the output path does any dst_output() calls, from Eli Cooper. 6) bcmgenet uses wrong device struct pointer in DMA API calls, fix from Florian Fainelli. 7) Various netfilter nat bug fixes from FLorian Westphal. 8) Fix memory leak in ipvlan_link_new(), from Gao Feng. 9) Locking fixes, particularly wrt. socket lookups, in l2tp from Guillaume Nault. 10) Avoid invoking rhash teardowns in atomic context by moving netlink cb->done() dump completion from a worker thread. Fix from Herbert Xu. 11) Buffer refcount problems in tun and macvtap on errors, from Jason Wang. 12) We don't set Kconfig symbol DEFAULT_TCP_CONG properly when the user selects BBR. Fix from Julian Wollrath. 13) Fix deadlock in transmit path on altera TSE driver, from Lino Sanfilippo. 14) Fix unbalanced reference counting in dsa_switch_tree, from Nikita Yushchenko. 15) tc_tunnel_key needs to be properly exported to userspace via uapi, fix from Roi Dayan. 16) rds_tcp_init_net() doesn't unregister notifier in error path, fix from Sowmini Varadhan. 17) Stale packet header pointer access after pskb_expand_head() in genenve driver, fix from Sabrina Dubroca. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net: (103 commits) net: avoid signed overflows for SO_{SND|RCV}BUFFORCE geneve: avoid use-after-free of skb->data tipc: check minimum bearer MTU net: renesas: ravb: unintialized return value sh_eth: remove unchecked interrupts for RZ/A1 net: bcmgenet: Utilize correct struct device for all DMA operations NET: usb: qmi_wwan: add support for Telit LE922A PID 0x1040 cdc_ether: Fix handling connection notification ip6_offload: check segs for NULL in ipv6_gso_segment. RDS: TCP: unregister_netdevice_notifier() in error path of rds_tcp_init_net Revert: "ip6_tunnel: Update skb->protocol to ETH_P_IPV6 in ip6_tnl_xmit()" ipv6: Set skb->protocol properly for local output ipv4: Set skb->protocol properly for local output packet: fix race condition in packet_set_ring net: ethernet: altera: TSE: do not use tx queue lock in tx completion handler net: ethernet: altera: TSE: Remove unneeded dma sync for tx buffers net: ethernet: stmmac: fix of-node and fixed-link-phydev leaks net: ethernet: stmmac: platform: fix outdated function header net: ethernet: stmmac: dwmac-meson8b: fix probe error path net: ethernet: stmmac: dwmac-generic: fix probe error path ...
| * net/sched: Export tc_tunnel_key so its UAPI accessibleRoi Dayan2016-11-28
| | | | | | | | | | | | | | | | Export tc_tunnel_key so it can be used from user space. Signed-off-by: Roi Dayan <roid@mellanox.com> Reviewed-by: Amir Vadai <amir@vadai.me> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Merge branch 'for-linus' of ↵Linus Torvalds2016-12-01
|\ \ | |/ |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input Pull input fixes from Dmitry Torokhov: "We are disabling automatic probing of BYD touchpads as it results in too many false positives, and the hardware is not terribly popular and having the protocol support does not result in significantly improved user experience. We also change keycode for KEY_DATA to avoid clashing with KEY_FASTREVERSE. Luckily this newish code is used by CEC framework that is still in staging, so it is extremely unlikely that someone has already started using this keycode" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: Input: change KEY_DATA from 0x275 to 0x277 Input: psmouse - disable automatic probing of BYD touchpads
| * Input: change KEY_DATA from 0x275 to 0x277Ping Cheng2016-11-30
| | | | | | | | | | | | | | | | | | 0x275 is used by KEY_FASTREVERSE. Fixes: 488326947cd1 ("Input: add HDMI CEC specific keycodes") Signed-off-by: Ping Cheng <ping.cheng@wacom.com> Acked-by: Hans Verkuil <hans.verkuil@cisco.com> Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
* | Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvmLinus Torvalds2016-11-19
|\ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Pull KVM fixes from Radim Krčmář: "ARM: - Fix handling of the 32bit cycle counter - Fix cycle counter filtering x86: - Fix a race leading to double unregistering of user notifiers - Amend oversight in kvm_arch_set_irq that turned Hyper-V code dead - Use SRCU around kvm_lapic_set_vapic_addr - Avoid recursive flushing of asynchronous page faults - Do not rely on deferred update in KVM_GET_CLOCK, which fixes #GP - Let userspace know that KVM_GET_CLOCK is useful with master clock; 4.9 changed the return value to better match the guest clock, but didn't provide means to let guests take advantage of it" * tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: kvm: x86: merge kvm_arch_set_irq and kvm_arch_set_irq_inatomic KVM: x86: fix missed SRCU usage in kvm_lapic_set_vapic_addr KVM: async_pf: avoid recursive flushing of work items kvm: kvmclock: let KVM_GET_CLOCK return whether the master clock is in use KVM: Disable irq while unregistering user notifier KVM: x86: do not go through vcpu in __get_kvmclock_ns KVM: arm64: Fix the issues when guest PMCCFILTR is configured arm64: KVM: pmu: Fix AArch32 cycle counter access
| * | kvm: kvmclock: let KVM_GET_CLOCK return whether the master clock is in usePaolo Bonzini2016-11-19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Userspace can read the exact value of kvmclock by reading the TSC and fetching the timekeeping parameters out of guest memory. This however is brittle and not necessary anymore with KVM 4.11. Provide a mechanism that lets userspace know if the new KVM_GET_CLOCK semantics are in effect, and---since we are at it---if the clock is stable across all VCPUs. Cc: Radim Krčmář <rkrcmar@redhat.com> Cc: Marcelo Tosatti <mtosatti@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
* | | Revert "include/uapi/linux/atm_zatm.h: include linux/time.h"Mike Frysinger2016-11-13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This reverts commit cf00713a655d ("include/uapi/linux/atm_zatm.h: include linux/time.h"). This attempted to fix userspace breakage that no longer existed when the patch was merged. Almost one year earlier, commit 70ba07b675b5 ("atm: remove 'struct zatm_t_hist'") deleted the struct in question. After this patch was merged, we now have to deal with people being unable to include this header in conjunction with standard C library headers like stdlib.h (which linux-atm does). Example breakage: x86_64-pc-linux-gnu-gcc -DHAVE_CONFIG_H -I. -I../.. -I./../q2931 -I./../saal \ -I. -DCPPFLAGS_TEST -I../../src/include -O2 -march=native -pipe -g \ -frecord-gcc-switches -freport-bug -Wimplicit-function-declaration \ -Wnonnull -Wstrict-aliasing -Wparentheses -Warray-bounds \ -Wfree-nonheap-object -Wreturn-local-addr -fno-strict-aliasing -Wall \ -Wshadow -Wpointer-arith -Wwrite-strings -Wstrict-prototypes -c zntune.c In file included from /usr/include/linux/atm_zatm.h:17:0, from zntune.c:17: /usr/include/linux/time.h:9:8: error: redefinition of ‘struct timespec’ struct timespec { ^ In file included from /usr/include/sys/select.h:43:0, from /usr/include/sys/types.h:219, from /usr/include/stdlib.h:314, from zntune.c:9: /usr/include/time.h:120:8: note: originally defined here struct timespec ^ Signed-off-by: Mike Frysinger <vapier@gentoo.org> Acked-by: Mikko Rapeli <mikko.rapeli@iki.fi> Signed-off-by: David S. Miller <davem@davemloft.net>
* | | net: bpqether.h: remove if_ether.h guardBaruch Siach2016-11-13
|/ / | | | | | | | | | | | | | | __LINUX_IF_ETHER_H is not defined anywhere, and if_ether.h can keep itself from double inclusion, though it uses a single underscore prefix. Signed-off-by: Baruch Siach <baruch@tkos.co.il> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/netLinus Torvalds2016-10-29
|\ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Pull networking fixes from David Miller: "Lots of fixes, mostly drivers as is usually the case. 1) Don't treat zero DMA address as invalid in vmxnet3, from Alexey Khoroshilov. 2) Fix element timeouts in netfilter's nft_dynset, from Anders K. Pedersen. 3) Don't put aead_req crypto struct on the stack in mac80211, from Ard Biesheuvel. 4) Several uninitialized variable warning fixes from Arnd Bergmann. 5) Fix memory leak in cxgb4, from Colin Ian King. 6) Fix bpf handling of VLAN header push/pop, from Daniel Borkmann. 7) Several VRF semantic fixes from David Ahern. 8) Set skb->protocol properly in ip6_tnl_xmit(), from Eli Cooper. 9) Socket needs to be locked in udp_disconnect(), from Eric Dumazet. 10) Div-by-zero on 32-bit fix in mlx4 driver, from Eugenia Emantayev. 11) Fix stale link state during failover in NCSCI driver, from Gavin Shan. 12) Fix netdev lower adjacency list traversal, from Ido Schimmel. 13) Propvide proper handle when emitting notifications of filter deletes, from Jamal Hadi Salim. 14) Memory leaks and big-endian issues in rtl8xxxu, from Jes Sorensen. 15) Fix DESYNC_FACTOR handling in ipv6, from Jiri Bohac. 16) Several routing offload fixes in mlxsw driver, from Jiri Pirko. 17) Fix broadcast sync problem in TIPC, from Jon Paul Maloy. 18) Validate chunk len before using it in SCTP, from Marcelo Ricardo Leitner. 19) Revert a netns locking change that causes regressions, from Paul Moore. 20) Add recursion limit to GRO handling, from Sabrina Dubroca. 21) GFP_KERNEL in irq context fix in ibmvnic, from Thomas Falcon. 22) Avoid accessing stale vxlan/geneve socket in data path, from Pravin Shelar" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net: (189 commits) geneve: avoid using stale geneve socket. vxlan: avoid using stale vxlan socket. qede: Fix out-of-bound fastpath memory access net: phy: dp83848: add dp83822 PHY support enic: fix rq disable tipc: fix broadcast link synchronization problem ibmvnic: Fix missing brackets in init_sub_crq_irqs ibmvnic: Fix releasing of sub-CRQ IRQs in interrupt context Revert "ibmvnic: Fix releasing of sub-CRQ IRQs in interrupt context" arch/powerpc: Update parameters for csum_tcpudp_magic & csum_tcpudp_nofold net/mlx4_en: Save slave ethtool stats command net/mlx4_en: Fix potential deadlock in port statistics flow net/mlx4: Fix firmware command timeout during interrupt test net/mlx4_core: Do not access comm channel if it has not yet been initialized net/mlx4_en: Fix panic during reboot net/mlx4_en: Process all completions in RX rings after port goes up net/mlx4_en: Resolve dividing by zero in 32-bit system net/mlx4_core: Change the default value of enable_qos net/mlx4_core: Avoid setting ports to auto when only one port type is supported net/mlx4_core: Fix the resource-type enum in res tracker to conform to FW spec ...
| * | rtnetlink: Add rtnexthop offload flag to compare maskJiri Pirko2016-10-19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The offload flag is a status flag and should not be used by FIB semantics for comparison. Fixes: 37ed9493699c ("rtnetlink: add RTNH_F_EXTERNAL flag for fib offload") Signed-off-by: Jiri Pirko <jiri@mellanox.com> Reviewed-by: Andy Gospodarek <andy@greyhouse.net> Signed-off-by: David S. Miller <davem@davemloft.net>
| * | ethtool: silence warning on bit lossJesse Brandeburg2016-10-14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Sparse was complaining when we went to prototype some code using ethtool_cmd_speed_set and SPEED_100000, which uses the upper 16 bits of __u32 speed for the first time. CHECK ... .../uapi/linux/ethtool.h:123:28: warning: cast truncates bits from constant value (186a0 becomes 86a0) The warning is actually bogus, as no bits are really lost, but we can get rid of the sparse warning with this one small change. Reported-by: Preethi Banala <preethi.banala@intel.com> Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | | Merge tag 'for-linus-4.9-2' of git://git.code.sf.net/p/openipmi/linux-ipmiLinus Torvalds2016-10-23
|\ \ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Pull IPMI updates from Corey Minyard: "A small bug fix and a new driver for acting as an IPMI device. I was on vacation during the merge window (a long vacation) but this is a bug fix that should go in and a new driver that shouldn't hurt anything. This has been in linux-next for a month or so" * tag 'for-linus-4.9-2' of git://git.code.sf.net/p/openipmi/linux-ipmi: ipmi: fix crash on reading version from proc after unregisted bmc ipmi/bt-bmc: remove redundant return value check of platform_get_resource() ipmi/bt-bmc: add a dependency on ARCH_ASPEED ipmi: Fix ioremap error handling in bt-bmc ipmi: add an Aspeed BT IPMI BMC driver
| * | | ipmi: add an Aspeed BT IPMI BMC driverAlistair Popple2016-09-29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This patch adds a simple device driver to expose the iBT interface on Aspeed SOCs (AST2400 and AST2500) as a character device. Such SOCs are commonly used as BMCs (BaseBoard Management Controllers) and this driver implements the BMC side of the BT interface. The BT (Block Transfer) interface is used to perform in-band IPMI communication between a host and its BMC. Entire messages are buffered before sending a notification to the other end, host or BMC, that there is data to be read. Usually, the host emits requests and the BMC responses but the specification provides a mean for the BMC to send SMS Attention (BMC-to-Host attention or System Management Software attention) messages. For this purpose, the driver introduces a specific ioctl on the device: 'BT_BMC_IOCTL_SMS_ATN' that can be used by the system running on the BMC to signal the host of such an event. The device name defaults to '/dev/ipmi-bt-host' Signed-off-by: Alistair Popple <alistair@popple.id.au> Signed-off-by: Jeremy Kerr <jk@ozlabs.org> Signed-off-by: Joel Stanley <joel@jms.id.au> [clg: - checkpatch fixes - added a devicetree binding documentation - replace 'bt_host' by 'bt_bmc' to reflect that the driver is the BMC side of the IPMI BT interface - renamed the device to 'ipmi-bt-host' - introduced a temporary buffer to copy_{to,from}_user - used platform_get_irq() - moved the driver under drivers/char/ipmi/ but kept it as a misc device - changed the compatible cell to "aspeed,ast2400-bt-bmc" ] Signed-off-by: Cédric Le Goater <clg@kaod.org> Acked-by: Arnd Bergmann <arnd@arndb.de> [clg: - checkpatch --strict fixes - removed the use of devm_iounmap, devm_kfree in cleanup paths - introduced an atomic-t to limit opens to 1 - introduced a mutex to protect write/read operations] Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Corey Minyard <cminyard@mvista.com>
* | | | Merge branch 'for-linus-4.9' of ↵Linus Torvalds2016-10-14
|\ \ \ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs Pull btrfs fixes from Chris Mason: "Some fixes from Omar and Dave Sterba for our new free space tree. This isn't heavily used yet, but as we move toward making it the new default we wanted to nail down an endian bug" * 'for-linus-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs: btrfs: tests: uninline member definitions in free_space_extent btrfs: tests: constify free space extent specs Btrfs: expand free space tree sanity tests to catch endianness bug Btrfs: fix extent buffer bitmap tests on big-endian systems Btrfs: catch invalid free space trees Btrfs: fix mount -o clear_cache,space_cache=v2 Btrfs: fix free space tree bitmaps on big-endian systems
| * | | | Btrfs: catch invalid free space treesOmar Sandoval2016-10-03
| |/ / / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | There are two separate issues that can lead to corrupted free space trees. 1. The free space tree bitmaps had an endianness issue on big-endian systems which is fixed by an earlier patch in this series. 2. btrfs-progs before v4.7.3 modified filesystems without updating the free space tree. To catch both of these issues at once, we need to force the free space tree to be rebuilt. To do so, add a FREE_SPACE_TREE_VALID compat_ro bit. If the bit isn't set, we know that it was either produced by a broken big-endian kernel or may have been corrupted by btrfs-progs. This also provides us with a way to add rudimentary read-write support for the free space tree to btrfs-progs: it can just clear this bit and have the kernel rebuild the free space tree. Cc: stable@vger.kernel.org # 4.5+ Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com> Tested-by: Chandan Rajendra <chandan@linux.vnet.ibm.com> Signed-off-by: Omar Sandoval <osandov@fb.com> Signed-off-by: David Sterba <dsterba@suse.com>
* | / / qedr: Add RoCE driver frameworkRam Amrani2016-10-14
| |/ / |/| | | | | | | | | | | | | | | | | | | | | | | | | | Adds a skeletal implementation of the qed* RoCE driver - basically the ability to communicate with the qede driver and receive notifications from it regarding various init/exit events. Signed-off-by: Rajesh Borundia <rajesh.borundia@cavium.com> Signed-off-by: Ram Amrani <Ram.Amrani@cavium.com> Signed-off-by: Doug Ledford <dledford@redhat.com>
* | | Merge tag 'nfsd-4.9' of git://linux-nfs.org/~bfields/linuxLinus Torvalds2016-10-14
|\ \ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Pull nfsd updates from Bruce Fields: "Some RDMA work and some good bugfixes, and two new features that could benefit from user testing: - Anna Schumacker contributed a simple NFSv4.2 COPY implementation. COPY is already supported on the client side, so a call to copy_file_range() on a recent client should now result in a server-side copy that doesn't require all the data to make a round trip to the client and back. - Jeff Layton implemented callbacks to notify clients when contended locks become available, which should reduce latency on workloads with contended locks" * tag 'nfsd-4.9' of git://linux-nfs.org/~bfields/linux: NFSD: Implement the COPY call nfsd: handle EUCLEAN nfsd: only WARN once on unmapped errors exportfs: be careful to only return expected errors. nfsd4: setclientid_confirm with unmatched verifier should fail nfsd: randomize SETCLIENTID reply to help distinguish servers nfsd: set the MAY_NOTIFY_LOCK flag in OPEN replies nfs: add a new NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK constant nfsd: add a LRU list for blocked locks nfsd: have nfsd4_lock use blocking locks for v4.1+ locks nfsd: plumb in a CB_NOTIFY_LOCK operation NFSD: fix corruption in notifier registration svcrdma: support Remote Invalidation svcrdma: Server-side support for rpcrdma_connect_private rpcrdma: RDMA/CM private message data structure svcrdma: Skip put_page() when send_reply() fails svcrdma: Tail iovec leaves an orphaned DMA mapping nfsd: fix dprintk in nfsd4_encode_getdeviceinfo nfsd: eliminate cb_minorversion field nfsd: don't set a FL_LAYOUT lease for flexfiles layouts
| * | | nfs: add a new NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK constantJeff Layton2016-09-26
| |/ / | | | | | | | | | | | | | | | | | | As defined in RFC 5661, section 18.16. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
* | | Merge tag 'xfs-reflink-for-linus-4.9-rc1' of ↵Linus Torvalds2016-10-13
|\ \ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs < XFS has gained super CoW powers! > ---------------------------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || Pull XFS support for shared data extents from Dave Chinner: "This is the second part of the XFS updates for this merge cycle. This pullreq contains the new shared data extents feature for XFS. Given the complexity and size of this change I am expecting - like the addition of reverse mapping last cycle - that there will be some follow-up bug fixes and cleanups around the -rc3 stage for issues that I'm sure will show up once the code hits a wider userbase. What it is: At the most basic level we are simply adding shared data extents to XFS - i.e. a single extent on disk can now have multiple owners. To do this we have to add new on-disk features to both track the shared extents and the number of times they've been shared. This is done by the new "refcount" btree that sits in every allocation group. When we share or unshare an extent, this tree gets updated. Along with this new tree, the reverse mapping tree needs to be updated to track each owner or a shared extent. This also needs to be updated ever share/unshare operation. These interactions at extent allocation and freeing time have complex ordering and recovery constraints, so there's a significant amount of new intent-based transaction code to ensure that operations are performed atomically from both the runtime and integrity/crash recovery perspectives. We also need to break sharing when writes hit a shared extent - this is where the new copy-on-write implementation comes in. We allocate new storage and copy the original data along with the overwrite data into the new location. We only do this for data as we don't share metadata at all - each inode has it's own metadata that tracks the shared data extents, the extents undergoing CoW and it's own private extents. Of course, being XFS, nothing is simple - we use delayed allocation for CoW similar to how we use it for normal writes. ENOSPC is a significant issue here - we build on the reservation code added in 4.8-rc1 with the reverse mapping feature to ensure we don't get spurious ENOSPC issues part way through a CoW operation. These mechanisms also help minimise fragmentation due to repeated CoW operations. To further reduce fragmentation overhead, we've also introduced a CoW extent size hint, which indicates how large a region we should allocate when we execute a CoW operation. With all this functionality in place, we can hook up .copy_file_range, .clone_file_range and .dedupe_file_range and we gain all the capabilities of reflink and other vfs provided functionality that enable manipulation to shared extents. We also added a fallocate mode that explicitly unshares a range of a file, which we implemented as an explicit CoW of all the shared extents in a file. As such, it's a huge chunk of new functionality with new on-disk format features and internal infrastructure. It warns at mount time as an experimental feature and that it may eat data (as we do with all new on-disk features until they stabilise). We have not released userspace suport for it yet - userspace support currently requires download from Darrick's xfsprogs repo and build from source, so the access to this feature is really developer/tester only at this point. Initial userspace support will be released at the same time the kernel with this code in it is released. The new code causes 5-6 new failures with xfstests - these aren't serious functional failures but things the output of tests changing slightly due to perturbations in layouts, space usage, etc. OTOH, we've added 150+ new tests to xfstests that specifically exercise this new functionality so it's got far better test coverage than any functionality we've previously added to XFS. Darrick has done a pretty amazing job getting us to this stage, and special mention also needs to go to Christoph (review, testing, improvements and bug fixes) and Brian (caught several intricate bugs during review) for the effort they've also put in. Summary: - unshare range (FALLOC_FL_UNSHARE) support for fallocate - copy-on-write extent size hints (FS_XFLAG_COWEXTSIZE) for fsxattr interface - shared extent support for XFS - copy-on-write support for shared extents - copy_file_range support - clone_file_range support (implements reflink) - dedupe_file_range support - defrag support for reverse mapping enabled filesystems" * tag 'xfs-reflink-for-linus-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs: (71 commits) xfs: convert COW blocks to real blocks before unwritten extent conversion xfs: rework refcount cow recovery error handling xfs: clear reflink flag if setting realtime flag xfs: fix error initialization xfs: fix label inaccuracies xfs: remove isize check from unshare operation xfs: reduce stack usage of _reflink_clear_inode_flag xfs: check inode reflink flag before calling reflink functions xfs: implement swapext for rmap filesystems xfs: refactor swapext code xfs: various swapext cleanups xfs: recognize the reflink feature bit xfs: simulate per-AG reservations being critically low xfs: don't mix reflink and DAX mode for now xfs: check for invalid inode reflink flags xfs: set a default CoW extent size of 32 blocks xfs: convert unwritten status of reverse mappings for shared files xfs: use interval query for rmap alloc operations on shared files xfs: add shared rmap map/unmap/convert log item types xfs: increase log reservations for reflink ...
| * | | vfs: add a FALLOC_FL_UNSHARE mode to fallocate to unshare a range of blocksDarrick J. Wong2016-10-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Add a new fallocate mode flag that explicitly unshares blocks on filesystems that support such features. The new flag can only be used with an allocate-mode fallocate call. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
| * | | vfs: support FS_XFLAG_COWEXTSIZE and get/set of CoW extent size hintDarrick J. Wong2016-10-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Introduce XFLAGs for the new XFS CoW extent size hint, and actually plumb the CoW extent size hint into the fsxattr structure. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
* | | | Merge tag 'drm-for-v4.9' of git://people.freedesktop.org/~airlied/linuxLinus Torvalds2016-10-11
|\ \ \ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Pull drm updates from Dave Airlie: "Core: - Fence destaging work - DRIVER_LEGACY to split off legacy drm drivers - drm_mm refactoring - Splitting drm_crtc.c into chunks and documenting better - Display info fixes - rbtree support for prime buffer lookup - Simple VGA DAC driver Panel: - Add Nexus 7 panel - More simple panels i915: - Refactoring GEM naming - Refactored vma/active tracking - Lockless request lookups - Better stolen memory support - FBC fixes - SKL watermark fixes - VGPU improvements - dma-buf fencing support - Better DP dongle support amdgpu: - Powerplay for Iceland asics - Improved GPU reset support - UVD/VEC powergating support for CZ/ST - Preinitialised VRAM buffer support - Virtual display support - Initial SI support - GTT rework - PCI shutdown callback support - HPD IRQ storm fixes amdkfd: - bugfixes tilcdc: - Atomic modesetting support mediatek: - AAL + GAMMA engine support - Hook up gamma LUT - Temporal dithering support imx: - Pixel clock from devicetree - drm bridge support for LVDS bridges - active plane reconfiguration - VDIC deinterlacer support - Frame synchronisation unit support - Color space conversion support analogix: - PSR support - Better panel on/off support rockchip: - rk3399 vop/crtc support - PSR support vc4: - Interlaced vblank timing - 3D rendering CPU overhead reduction - HDMI output fixes tda998x: - HDMI audio ASoC support sunxi: - Allwinner A33 support - better TCON support msm: - DT binding cleanups - Explicit fence-fd support sti: - remove sti415/416 support etnaviv: - MMUv2 refactoring - GC3000 support exynos: - Refactoring HDMI DCC/PHY - G2D pm regression fix - Page fault issues with wait for vblank There is no nouveau work in this tree, as Ben didn't get a pull request in, and he was fighting moving to atomic and adding mst support, so maybe best it waits for a cycle" * tag 'drm-for-v4.9' of git://people.freedesktop.org/~airlied/linux: (1412 commits) drm/crtc: constify drm_crtc_index parameter drm/i915: Fix conflict resolution from backmerge of v4.8-rc8 to drm-next drm/i915/guc: Unwind GuC workqueue reservation if request construction fails drm/i915: Reset the breadcrumbs IRQ more carefully drm/i915: Force relocations via cpu if we run out of idle aperture drm/i915: Distinguish last emitted request from last submitted request drm/i915: Allow DP to work w/o EDID drm/i915: Move long hpd handling into the hotplug work drm/i915/execlists: Reinitialise context image after GPU hang drm/i915: Use correct index for backtracking HUNG semaphores drm/i915: Unalias obj->phys_handle and obj->userptr drm/i915: Just clear the mmiodebug before a register access drm/i915/gen9: only add the planes actually affected by ddb changes drm/i915: Allow PCH DPLL sharing regardless of DPLL_SDVO_HIGH_SPEED drm/i915/bxt: Fix HDMI DPLL configuration drm/i915/gen9: fix the watermark res_blocks value drm/i915/gen9: fix plane_blocks_per_line on watermarks calculations drm/i915/gen9: minimum scanlines for Y tile is not always 4 drm/i915/gen9: fix the WaWmMemoryReadLatency implementation drm/i915/kbl: KBL also needs to run the SAGV code ...
| * | | | uapi: add missing install of sync_file.hEmilio López2016-10-04
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | As part of the sync framework destaging, the sync_file.h header was moved, but an entry was not added on Kbuild to install it. This patch resolves this omission so that "make headers_install" installs this header. Fixes: 460bfc41fd52 ("dma-buf/sync_file: de-stage sync_file headers") Reported-by: Michael Ellerman <mpe@ellerman.id.au> Reviewed-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk> Signed-off-by: Emilio López <emilio.lopez@collabora.co.uk> Signed-off-by: Sean Paul <seanpaul@chromium.org> Link: http://patchwork.freedesktop.org/patch/msgid/20160927143142.8975-1-emilio.lopez@collabora.co.uk
| * | | | Merge tag 'v4.8-rc8' into drm-nextDave Airlie2016-09-27
| |\ \ \ \ | | | |/ / | | |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Linux 4.8-rc8 There was a lot of fallout in the imx/amdgpu/i915 drivers, so backmerge it now to avoid troubles. * tag 'v4.8-rc8': (1442 commits) Linux 4.8-rc8 fault_in_multipages_readable() throws set-but-unused error mm: check VMA flags to avoid invalid PROT_NONE NUMA balancing radix tree: fix sibling entry handling in radix_tree_descend() radix tree test suite: Test radix_tree_replace_slot() for multiorder entries fix memory leaks in tracing_buffers_splice_read() tracing: Move mutex to protect against resetting of seq data MIPS: Fix delay slot emulation count in debugfs MIPS: SMP: Fix possibility of deadlock when bringing CPUs online mm: delete unnecessary and unsafe init_tlb_ubc() huge tmpfs: fix Committed_AS leak shmem: fix tmpfs to handle the huge= option properly blk-mq: skip unmapped queues in blk_mq_alloc_request_hctx MIPS: Fix pre-r6 emulation FPU initialisation arm64: kgdb: handle read-only text / modules arm64: Call numa_store_cpu_info() earlier. locking/hung_task: Fix typo in CONFIG_DETECT_HUNG_TASK help text nvme-rdma: only clear queue flags after successful connect i2c: qup: skip qup_i2c_suspend if the device is already runtime suspended perf/core: Limit matching exclusive events to one PMU ...
| * | | | dma-buf/sync_file: fix documentation errorEmilio López2016-09-20
| | |/ / | |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The ioctl name and description on the documentation block don't match the ioctl being defined. This was probably overlooked while renaming the ioctls during the sync file destaging. This patch provides a more accurate description of what the ioctl actually does. Signed-off-by: Emilio López <emilio.lopez@collabora.co.uk> Reviewed-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> Link: http://patchwork.freedesktop.org/patch/msgid/20160919042120.6280-1-emilio.lopez@collabora.co.uk
* | | | Merge branch 'akpm' (patches from Andrew)Linus Torvalds2016-10-11
|\ \ \ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Merge more updates from Andrew Morton: - a few block updates that fell in my lap - lib/ updates - checkpatch - autofs - ipc - a ton of misc other things * emailed patches from Andrew Morton <akpm@linux-foundation.org>: (100 commits) mm: split gfp_mask and mapping flags into separate fields fs: use mapping_set_error instead of opencoded set_bit treewide: remove redundant #include <linux/kconfig.h> hung_task: allow hung_task_panic when hung_task_warnings is 0 kthread: add kerneldoc for kthread_create() kthread: better support freezable kthread workers kthread: allow to modify delayed kthread work kthread: allow to cancel kthread work kthread: initial support for delayed kthread work kthread: detect when a kthread work is used by more workers kthread: add kthread_destroy_worker() kthread: add kthread_create_worker*() kthread: allow to call __kthread_create_on_node() with va_list args kthread/smpboot: do not park in kthread_create_on_cpu() kthread: kthread worker API cleanup kthread: rename probe_kthread_data() to kthread_probe_data() scripts/tags.sh: enable code completion in VIM mm: kmemleak: avoid using __va() on addresses that don't have a lowmem mapping kdump, vmcoreinfo: report memory sections virtual addresses ipc/sem.c: add cond_resched in exit_sme ...
| * | | | autofs4: move linux/auto_dev-ioctl.h to uapi/linuxIan Kent2016-10-11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Since linux/auto_dev-ioctl.h wasn't included in include/linux/Kbuild it wasn't moved to uapi/linux as part of the uapi series. Link: http://lkml.kernel.org/r/20160812024901.12352.10984.stgit@pluto.themaw.net Signed-off-by: Ian Kent <raven@themaw.net> Cc: Tomohiro Kusumi <kusumi.tomohiro@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| * | | | autofs: move inclusion of linux/limits.h to uapiTomohiro Kusumi2016-10-11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | linux/limits.h should be included by uapi instead of linux/auto_fs.h so as not to cause compile error in userspace. # cat << EOF > ./test1.c > #include <stdio.h> > #include <linux/auto_fs.h> > int main(void) { > return 0; > } > EOF # gcc -Wall -g ./test1.c In file included from ./test1.c:2:0: /usr/include/linux/auto_fs.h:54:12: error: 'NAME_MAX' undeclared here (not in a function) char name[NAME_MAX+1]; ^ Link: http://lkml.kernel.org/r/20160812024856.12352.24092.stgit@pluto.themaw.net Signed-off-by: Tomohiro Kusumi <kusumi.tomohiro@gmail.com> Signed-off-by: Ian Kent <ikent@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
* | | | | Merge tag 'media/v4.9-1' of ↵Linus Torvalds2016-10-11
|\ \ \ \ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media Pull media updates from Mauro Carvalho Chehab: - Documentation improvements: conversion of all non-DocBook documents to Sphinx and lots of fixes to the uAPI media book - New PCI driver for Techwell TW5864 media grabber boards - New SoC driver for ATMEL Image Sensor Controller - Removal of some obsolete SoC drivers (s5p-tv driver and soc_camera drivers) - Addition of ST CEC driver - Lots of drivers fixes, improvements and additions * tag 'media/v4.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (464 commits) [media] ttusb_dec: avoid the risk of go past buffer [media] cx23885: Fix some smatch warnings [media] si2165: switch to regmap [media] si2165: use i2c_client->dev instead of i2c_adapter->dev for logging [media] si2165: Remove legacy attach [media] cx231xx: attach si2165 driver via i2c_client [media] cx231xx: Prepare for attaching new style i2c_client DVB demod drivers [media] cx23885: attach si2165 driver via i2c_client [media] si2165: support i2c_client attach [media] si2165: avoid division by zero [media] rcar-vin: add R-Car gen2 fallback compatibility string [media] lgdt3306a: remove 20*50 msec unnecessary timeout [media] cx25821: Remove deprecated create_singlethread_workqueue [media] cx25821: Drop Freeing of Workqueue [media] cxd2841er: force 8MHz bandwidth for DVB-C if specified bw not supported [media] redrat3: hardware-specific parameters [media] redrat3: remove hw_timeout member [media] cxd2841er: BER and SNR reading for ISDB-T [media] dvb-usb: avoid link error with dib3000m{b,c| [media] dvb-usb: split out common parts of dibusb ...
| * \ \ \ \ Merge tag 'v4.8' into patchworkMauro Carvalho Chehab2016-10-05
| |\ \ \ \ \ | | | |_|/ / | | |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Linux 4.8 * tag 'v4.8': (1761 commits) Linux 4.8 ARM: 8618/1: decompressor: reset ttbcr fields to use TTBR0 on ARMv7 MIPS: CM: Fix mips_cm_max_vp_width for non-MT kernels on MT systems include/linux/property.h: fix typo/compile error ocfs2: fix deadlock on mmapped page in ocfs2_write_begin_nolock() mm: workingset: fix crash in shadow node shrinker caused by replace_page_cache_page() MAINTAINERS: Switch to kernel.org email address for Javi Merino x86/entry/64: Fix context tracking state warning when load_gs_index fails x86/boot: Initialize FPU and X86_FEATURE_ALWAYS even if we don't have CPUID x86/vdso: Fix building on big endian host x86/boot: Fix another __read_cr4() case on 486 sctp: fix the issue sctp_diag uses lock_sock in rcu_read_lock sctp: change to check peer prsctp_capable when using prsctp polices sctp: remove prsctp_param from sctp_chunk sctp: move sent_count to the memory hole in sctp_chunk tg3: Avoid NULL pointer dereference in tg3_io_error_detected() x86/init: Fix cr4_init_shadow() on CR4-less machines MIPS: Fix detection of unsupported highmem with cache aliases MIPS: Malta: Fix IOCU disable switch read for MIPS64 MIPS: Fix BUILD_ROLLBACK_PROLOGUE for microMIPS ...
| * | | | | [media] SDI: add flag for SDI formats and SMPTE 125M definitionCharles-Antoine Couret2016-09-19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Adding others generic flags, which could be used by many components like GS1662. Signed-off-by: Charles-Antoine Couret <charles-antoine.couret@nexvision.fr> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
| * | | | | [media] media: Add 1X16 16-bit raw bayer media bus code definitionsSakari Ailus2016-09-09
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The codes will be called: MEDIA_BUS_FMT_SBGGR16_1X16 MEDIA_BUS_FMT_SGBRG16_1X16 MEDIA_BUS_FMT_SGRBG16_1X16 MEDIA_BUS_FMT_SRGGB16_1X16 Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
| * | | | | [media] media: Add 1X14 14-bit raw bayer media bus code definitionsJouni Ukkonen2016-09-09
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The codes will be called: MEDIA_BUS_FMT_SBGGR14_1X14 MEDIA_BUS_FMT_SGBRG14_1X14 MEDIA_BUS_FMT_SGRBG14_1X14 MEDIA_BUS_FMT_SRGGB14_1X14 Signed-off-by: Jouni Ukkonen <jouni.ukkonen@intel.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Acked-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
| * | | | | [media] fix broken references on dvb/video*rstMauro Carvalho Chehab2016-09-09
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Trivially fix those broken references, by copying the structs fron the header, just like other API documentation at the DVB side. This doesn't have the level of quality used at the V4L2 side of the API, but, as this documents a deprecated API, used only by av7110 driver, it doesn't make much sense to invest time making it better. Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
| * | | | | [media] videodev2.h: put V4L2_YCBCR_ENC_SYCC under #ifndef __KERNEL__Hans Verkuil2016-08-24
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This define is a duplicate of V4L2_YCBCR_ENC_601. So mark it as 'should not be used' and put it under #ifndef __KERNEL__ to prevent drivers from referring to it. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
| * | | | | [media] videodev2.h: fix sYCC/AdobeYCC default quantization rangeHans Verkuil2016-08-24
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The default quantization range of the Y'CbCr encodings of sRGB and AdobeRGB are full range instead of limited range according to the corresponding standards. Fix this in the V4L2_MAP_QUANTIZATION_DEFAULT macro. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
| * | | | | [media] v4l2-core: Add support for touch devicesNick Dyer2016-08-23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Some touch controllers send out touch data in a similar way to a greyscale frame grabber. Add new device type VFL_TYPE_TOUCH: - This uses a new device prefix v4l-touch for these devices, to stop generic capture software from treating them as webcams. Otherwise, touch is treated similarly to video capture. - Add V4L2_INPUT_TYPE_TOUCH - Add MEDIA_INTF_T_V4L_TOUCH - Add V4L2_CAP_TOUCH to indicate device is a touch device Add formats: - V4L2_TCH_FMT_DELTA_TD16 for signed 16-bit touch deltas - V4L2_TCH_FMT_DELTA_TD08 for signed 16-bit touch deltas - V4L2_TCH_FMT_TU16 for unsigned 16-bit touch data - V4L2_TCH_FMT_TU08 for unsigned 8-bit touch data This support will be used by: - Atmel maXTouch (atmel_mxt_ts) - Synaptics RMI4. - sur40 Signed-off-by: Nick Dyer <nick@shmanahar.org> Tested-by: Chris Healy <cphealy@gmail.com> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com> Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
* | | | | | Merge tag 'libnvdimm-for-4.9' of ↵Linus Torvalds2016-10-11
|\ \ \ \ \ \ | |_|_|/ / / |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm Pull libnvdimm updates from Dan Williams: "Aside from the recently added pmem sub-division support these have been in -next for several releases with no reported issues. The sub- division support was included in next-20161010 with no reported issues. It passes all unit tests including new tests for all the new functionality below. Summary: - PMEM sub-division support: Allow a single PMEM region to be divided into multiple namespaces. Originally, ~2 years ago, it was thought that partitions of a /dev/pmemX block device could handle sub-allocations of persistent memory for different use cases. With the decision to not support DAX mappings of raw block-devices, and the genesis of device-dax, the need for having multiple pmem-namespace per region has grown. - Device-DAX unified inode: In support of dynamic-resizing of a device-dax instance the kernel arranges for all mappings of a device-dax node to share the same inode. This allows unmap / truncate / invalidation events to affect all instances of the device similar to the behavior of mmap on block devices. - Hardware error scrubbing reworks: The original address-range-scrub and badblocks tracking solution allowed clearing entries at the individual namespace level, but it failed to clear the internal list of media errors maintained at the bus level. The result was that the next scrub or namespace disable/re-enable event would restore the cleared badblocks, but now that is fixed. The v4.8 kernel introduced an auto-scrub-on-machine-check behavior to repopulate the badblocks list. Now, in v4.9, the auto-scrub behavior can be disabled and simply arrange for the error reported in the machine-check to be added to the list. - DIMM health-event notification support: ACPI 6.1 defines a notification event code that can be send to ACPI NVDIMM devices. A poll(2) capable file descriptor for these events can be obtained from the nmemX/nfit/flags sysfs-attribute of a libnvdimm memory device. - Miscellaneous fixes: NVDIMM-N probe error, device-dax build error, and a change to dedup the flush hint list to not flush the memory controller more than necessary" * tag 'libnvdimm-for-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm: (39 commits) /dev/dax: fix Kconfig dependency build breakage dax: use correct dev_t value dax: convert devm_create_dax_dev to PTR_ERR libnvdimm, namespace: allow creation of multiple pmem-namespaces per region libnvdimm, namespace: lift single pmem limit in scan_labels() libnvdimm, namespace: filter out of range labels in scan_labels() libnvdimm, namespace: enable allocation of multiple pmem namespaces libnvdimm, namespace: update label implementation for multi-pmem libnvdimm, namespace: expand pmem device naming scheme for multi-pmem libnvdimm, region: update nd_region_available_dpa() for multi-pmem support libnvdimm, namespace: sort namespaces by dpa at init libnvdimm, namespace: allow multiple pmem-namespaces per region at scan time tools/testing/nvdimm: support for sub-dividing a pmem region libnvdimm, namespace: unify blk and pmem label scanning libnvdimm, namespace: refactor uuid_show() into a namespace_to_uuid() helper libnvdimm, label: convert label tracking to a linked list libnvdimm, region: move region-mapping input-paramters to nd_mapping_desc nvdimm: reduce duplicated wpq flushes libnvdimm: clear the internal poison_list when clearing badblocks pmem: reduce kmap_atomic sections to the memcpys only ...
| * | | | | Merge branch 'for-4.9/dax' into libnvdimm-for-nextDan Williams2016-10-07
| |\ \ \ \ \
| | * | | | | dax: define a unified inode/address_space for device-dax mappingsDan Williams2016-08-24
| | | |_|/ / | | |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In support of enabling resize / truncate of device-dax instances, define a pseudo-fs to provide a unified inode/address space for vm operations. Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208
/*

  Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers

  Copyright 1998-2001 by Leonard N. Zubkoff <lnz@dandelion.com>
  Portions Copyright 2002 by Mylex (An IBM Business Unit)

  This program is free software; you may redistribute and/or modify it under
  the terms of the GNU General Public License Version 2 as published by the
  Free Software Foundation.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  for complete details.

*/


#define DAC960_DriverVersion			"2.5.49"
#define DAC960_DriverDate			"21 Aug 2007"


#include <linux/module.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/blkpg.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/reboot.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/random.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include "DAC960.h"

#define DAC960_GAM_MINOR	252


static DAC960_Controller_T *DAC960_Controllers[DAC960_MaxControllers];
static int DAC960_ControllerCount;
static struct proc_dir_entry *DAC960_ProcDirectoryEntry;

static long disk_size(DAC960_Controller_T *p, int drive_nr)
{
	if (p->FirmwareType == DAC960_V1_Controller) {
		if (drive_nr >= p->LogicalDriveCount)
			return 0;
		return p->V1.LogicalDriveInformation[drive_nr].
			LogicalDriveSize;
	} else {
		DAC960_V2_LogicalDeviceInfo_T *i =
			p->V2.LogicalDeviceInformation[drive_nr];
		if (i == NULL)
			return 0;
		return i->ConfigurableDeviceSize;
	}
}

static int DAC960_open(struct inode *inode, struct file *file)
{
	struct gendisk *disk = inode->i_bdev->bd_disk;
	DAC960_Controller_T *p = disk->queue->queuedata;
	int drive_nr = (long)disk->private_data;

	if (p->FirmwareType == DAC960_V1_Controller) {
		if (p->V1.LogicalDriveInformation[drive_nr].
		    LogicalDriveState == DAC960_V1_LogicalDrive_Offline)
			return -ENXIO;
	} else {
		DAC960_V2_LogicalDeviceInfo_T *i =
			p->V2.LogicalDeviceInformation[drive_nr];
		if (!i || i->LogicalDeviceState == DAC960_V2_LogicalDevice_Offline)
			return -ENXIO;
	}

	check_disk_change(inode->i_bdev);

	if (!get_capacity(p->disks[drive_nr]))
		return -ENXIO;
	return 0;
}

static int DAC960_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
	struct gendisk *disk = bdev->bd_disk;
	DAC960_Controller_T *p = disk->queue->queuedata;
	int drive_nr = (long)disk->private_data;

	if (p->FirmwareType == DAC960_V1_Controller) {
		geo->heads = p->V1.GeometryTranslationHeads;
		geo->sectors = p->V1.GeometryTranslationSectors;
		geo->cylinders = p->V1.LogicalDriveInformation[drive_nr].
			LogicalDriveSize / (geo->heads * geo->sectors);
	} else {
		DAC960_V2_LogicalDeviceInfo_T *i =
			p->V2.LogicalDeviceInformation[drive_nr];
		switch (i->DriveGeometry) {
		case DAC960_V2_Geometry_128_32:
			geo->heads = 128;
			geo->sectors = 32;
			break;
		case DAC960_V2_Geometry_255_63:
			geo->heads = 255;
			geo->sectors = 63;
			break;
		default:
			DAC960_Error("Illegal Logical Device Geometry %d\n",
					p, i->DriveGeometry);
			return -EINVAL;
		}

		geo->cylinders = i->ConfigurableDeviceSize /
			(geo->heads * geo->sectors);
	}
	
	return 0;
}

static int DAC960_media_changed(struct gendisk *disk)
{
	DAC960_Controller_T *p = disk->queue->queuedata;
	int drive_nr = (long)disk->private_data;

	if (!p->LogicalDriveInitiallyAccessible[drive_nr])
		return 1;
	return 0;
}

static int DAC960_revalidate_disk(struct gendisk *disk)
{
	DAC960_Controller_T *p = disk->queue->queuedata;
	int unit = (long)disk->private_data;

	set_capacity(disk, disk_size(p, unit));
	return 0;
}

static struct block_device_operations DAC960_BlockDeviceOperations = {
	.owner			= THIS_MODULE,
	.open			= DAC960_open,
	.getgeo			= DAC960_getgeo,
	.media_changed		= DAC960_media_changed,
	.revalidate_disk	= DAC960_revalidate_disk,
};


/*
  DAC960_AnnounceDriver announces the Driver Version and Date, Author's Name,
  Copyright Notice, and Electronic Mail Address.
*/

static void DAC960_AnnounceDriver(DAC960_Controller_T *Controller)
{
  DAC960_Announce("***** DAC960 RAID Driver Version "
		  DAC960_DriverVersion " of "
		  DAC960_DriverDate " *****\n", Controller);
  DAC960_Announce("Copyright 1998-2001 by Leonard N. Zubkoff "
		  "<lnz@dandelion.com>\n", Controller);
}


/*
  DAC960_Failure prints a standardized error message, and then returns false.
*/

static bool DAC960_Failure(DAC960_Controller_T *Controller,
			      unsigned char *ErrorMessage)
{
  DAC960_Error("While configuring DAC960 PCI RAID Controller at\n",
	       Controller);
  if (Controller->IO_Address == 0)
    DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A "
		 "PCI Address 0x%X\n", Controller,
		 Controller->Bus, Controller->Device,
		 Controller->Function, Controller->PCI_Address);
  else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address "
		    "0x%X PCI Address 0x%X\n", Controller,
		    Controller->Bus, Controller->Device,
		    Controller->Function, Controller->IO_Address,
		    Controller->PCI_Address);
  DAC960_Error("%s FAILED - DETACHING\n", Controller, ErrorMessage);
  return false;
}

/*
  init_dma_loaf() and slice_dma_loaf() are helper functions for
  aggregating the dma-mapped memory for a well-known collection of
  data structures that are of different lengths.

  These routines don't guarantee any alignment.  The caller must
  include any space needed for alignment in the sizes of the structures
  that are passed in.
 */

static bool init_dma_loaf(struct pci_dev *dev, struct dma_loaf *loaf,
								 size_t len)
{
	void *cpu_addr;
	dma_addr_t dma_handle;

	cpu_addr = pci_alloc_consistent(dev, len, &dma_handle);
	if (cpu_addr == NULL)
		return false;
	
	loaf->cpu_free = loaf->cpu_base = cpu_addr;
	loaf->dma_free =loaf->dma_base = dma_handle;
	loaf->length = len;
	memset(cpu_addr, 0, len);
	return true;
}

static void *slice_dma_loaf(struct dma_loaf *loaf, size_t len,
					dma_addr_t *dma_handle)
{
	void *cpu_end = loaf->cpu_free + len;
	void *cpu_addr = loaf->cpu_free;

	BUG_ON(cpu_end > loaf->cpu_base + loaf->length);
	*dma_handle = loaf->dma_free;
	loaf->cpu_free = cpu_end;
	loaf->dma_free += len;
	return cpu_addr;
}

static void free_dma_loaf(struct pci_dev *dev, struct dma_loaf *loaf_handle)
{
	if (loaf_handle->cpu_base != NULL)
		pci_free_consistent(dev, loaf_handle->length,
			loaf_handle->cpu_base, loaf_handle->dma_base);
}


/*
  DAC960_CreateAuxiliaryStructures allocates and initializes the auxiliary
  data structures for Controller.  It returns true on success and false on
  failure.
*/

static bool DAC960_CreateAuxiliaryStructures(DAC960_Controller_T *Controller)
{
  int CommandAllocationLength, CommandAllocationGroupSize;
  int CommandsRemaining = 0, CommandIdentifier, CommandGroupByteCount;
  void *AllocationPointer = NULL;
  void *ScatterGatherCPU = NULL;
  dma_addr_t ScatterGatherDMA;
  struct pci_pool *ScatterGatherPool;
  void *RequestSenseCPU = NULL;
  dma_addr_t RequestSenseDMA;
  struct pci_pool *RequestSensePool = NULL;

  if (Controller->FirmwareType == DAC960_V1_Controller)
    {
      CommandAllocationLength = offsetof(DAC960_Command_T, V1.EndMarker);
      CommandAllocationGroupSize = DAC960_V1_CommandAllocationGroupSize;
      ScatterGatherPool = pci_pool_create("DAC960_V1_ScatterGather",
		Controller->PCIDevice,
	DAC960_V1_ScatterGatherLimit * sizeof(DAC960_V1_ScatterGatherSegment_T),
	sizeof(DAC960_V1_ScatterGatherSegment_T), 0);
      if (ScatterGatherPool == NULL)
	    return DAC960_Failure(Controller,
			"AUXILIARY STRUCTURE CREATION (SG)");
      Controller->ScatterGatherPool = ScatterGatherPool;
    }
  else
    {
      CommandAllocationLength = offsetof(DAC960_Command_T, V2.EndMarker);
      CommandAllocationGroupSize = DAC960_V2_CommandAllocationGroupSize;
      ScatterGatherPool = pci_pool_create("DAC960_V2_ScatterGather",
		Controller->PCIDevice,
	DAC960_V2_ScatterGatherLimit * sizeof(DAC960_V2_ScatterGatherSegment_T),
	sizeof(DAC960_V2_ScatterGatherSegment_T), 0);
      if (ScatterGatherPool == NULL)
	    return DAC960_Failure(Controller,
			"AUXILIARY STRUCTURE CREATION (SG)");
      RequestSensePool = pci_pool_create("DAC960_V2_RequestSense",
		Controller->PCIDevice, sizeof(DAC960_SCSI_RequestSense_T),
		sizeof(int), 0);
      if (RequestSensePool == NULL) {
	    pci_pool_destroy(ScatterGatherPool);
	    return DAC960_Failure(Controller,
			"AUXILIARY STRUCTURE CREATION (SG)");
      }
      Controller->ScatterGatherPool = ScatterGatherPool;
      Controller->V2.RequestSensePool = RequestSensePool;
    }
  Controller->CommandAllocationGroupSize = CommandAllocationGroupSize;
  Controller->FreeCommands = NULL;
  for (CommandIdentifier = 1;
       CommandIdentifier <= Controller->DriverQueueDepth;
       CommandIdentifier++)
    {
      DAC960_Command_T *Command;
      if (--CommandsRemaining <= 0)
	{
	  CommandsRemaining =
		Controller->DriverQueueDepth - CommandIdentifier + 1;
	  if (CommandsRemaining > CommandAllocationGroupSize)
		CommandsRemaining = CommandAllocationGroupSize;
	  CommandGroupByteCount =
		CommandsRemaining * CommandAllocationLength;
	  AllocationPointer = kzalloc(CommandGroupByteCount, GFP_ATOMIC);
	  if (AllocationPointer == NULL)
		return DAC960_Failure(Controller,
					"AUXILIARY STRUCTURE CREATION");
	 }
      Command = (DAC960_Command_T *) AllocationPointer;
      AllocationPointer += CommandAllocationLength;
      Command->CommandIdentifier = CommandIdentifier;
      Command->Controller = Controller;
      Command->Next = Controller->FreeCommands;
      Controller->FreeCommands = Command;
      Controller->Commands[CommandIdentifier-1] = Command;
      ScatterGatherCPU = pci_pool_alloc(ScatterGatherPool, GFP_ATOMIC,
							&ScatterGatherDMA);
      if (ScatterGatherCPU == NULL)
	  return DAC960_Failure(Controller, "AUXILIARY STRUCTURE CREATION");

      if (RequestSensePool != NULL) {
  	  RequestSenseCPU = pci_pool_alloc(RequestSensePool, GFP_ATOMIC,
						&RequestSenseDMA);
  	  if (RequestSenseCPU == NULL) {
                pci_pool_free(ScatterGatherPool, ScatterGatherCPU,
                                ScatterGatherDMA);
    		return DAC960_Failure(Controller,
					"AUXILIARY STRUCTURE CREATION");
	  }
        }
     if (Controller->FirmwareType == DAC960_V1_Controller) {
        Command->cmd_sglist = Command->V1.ScatterList;
	Command->V1.ScatterGatherList =
		(DAC960_V1_ScatterGatherSegment_T *)ScatterGatherCPU;
	Command->V1.ScatterGatherListDMA = ScatterGatherDMA;
      } else {
        Command->cmd_sglist = Command->V2.ScatterList;
	Command->V2.ScatterGatherList =
		(DAC960_V2_ScatterGatherSegment_T *)ScatterGatherCPU;
	Command->V2.ScatterGatherListDMA = ScatterGatherDMA;
	Command->V2.RequestSense =
				(DAC960_SCSI_RequestSense_T *)RequestSenseCPU;
	Command->V2.RequestSenseDMA = RequestSenseDMA;
      }
    }
  return true;
}


/*
  DAC960_DestroyAuxiliaryStructures deallocates the auxiliary data
  structures for Controller.
*/

static void DAC960_DestroyAuxiliaryStructures(DAC960_Controller_T *Controller)
{
  int i;
  struct pci_pool *ScatterGatherPool = Controller->ScatterGatherPool;
  struct pci_pool *RequestSensePool = NULL;
  void *ScatterGatherCPU;
  dma_addr_t ScatterGatherDMA;
  void *RequestSenseCPU;
  dma_addr_t RequestSenseDMA;
  DAC960_Command_T *CommandGroup = NULL;
  

  if (Controller->FirmwareType == DAC960_V2_Controller)
        RequestSensePool = Controller->V2.RequestSensePool;

  Controller->FreeCommands = NULL;
  for (i = 0; i < Controller->DriverQueueDepth; i++)
    {
      DAC960_Command_T *Command = Controller->Commands[i];

      if (Command == NULL)
	  continue;

      if (Controller->FirmwareType == DAC960_V1_Controller) {
	  ScatterGatherCPU = (void *)Command->V1.ScatterGatherList;
	  ScatterGatherDMA = Command->V1.ScatterGatherListDMA;
	  RequestSenseCPU = NULL;
	  RequestSenseDMA = (dma_addr_t)0;
      } else {
          ScatterGatherCPU = (void *)Command->V2.ScatterGatherList;
	  ScatterGatherDMA = Command->V2.ScatterGatherListDMA;
	  RequestSenseCPU = (void *)Command->V2.RequestSense;
	  RequestSenseDMA = Command->V2.RequestSenseDMA;
      }
      if (ScatterGatherCPU != NULL)
          pci_pool_free(ScatterGatherPool, ScatterGatherCPU, ScatterGatherDMA);
      if (RequestSenseCPU != NULL)
          pci_pool_free(RequestSensePool, RequestSenseCPU, RequestSenseDMA);

      if ((Command->CommandIdentifier
	   % Controller->CommandAllocationGroupSize) == 1) {
	   /*
	    * We can't free the group of commands until all of the
	    * request sense and scatter gather dma structures are free.
            * Remember the beginning of the group, but don't free it
	    * until we've reached the beginning of the next group.
	    */
	   kfree(CommandGroup);
	   CommandGroup = Command;
      }
      Controller->Commands[i] = NULL;
    }
  kfree(CommandGroup);

  if (Controller->CombinedStatusBuffer != NULL)
    {
      kfree(Controller->CombinedStatusBuffer);
      Controller->CombinedStatusBuffer = NULL;
      Controller->CurrentStatusBuffer = NULL;
    }

  if (ScatterGatherPool != NULL)
  	pci_pool_destroy(ScatterGatherPool);
  if (Controller->FirmwareType == DAC960_V1_Controller)
  	return;

  if (RequestSensePool != NULL)
	pci_pool_destroy(RequestSensePool);

  for (i = 0; i < DAC960_MaxLogicalDrives; i++) {
	kfree(Controller->V2.LogicalDeviceInformation[i]);
	Controller->V2.LogicalDeviceInformation[i] = NULL;
  }

  for (i = 0; i < DAC960_V2_MaxPhysicalDevices; i++)
    {
      kfree(Controller->V2.PhysicalDeviceInformation[i]);
      Controller->V2.PhysicalDeviceInformation[i] = NULL;
      kfree(Controller->V2.InquiryUnitSerialNumber[i]);
      Controller->V2.InquiryUnitSerialNumber[i] = NULL;
    }
}


/*
  DAC960_V1_ClearCommand clears critical fields of Command for DAC960 V1
  Firmware Controllers.
*/

static inline void DAC960_V1_ClearCommand(DAC960_Command_T *Command)
{
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  memset(CommandMailbox, 0, sizeof(DAC960_V1_CommandMailbox_T));
  Command->V1.CommandStatus = 0;
}


/*
  DAC960_V2_ClearCommand clears critical fields of Command for DAC960 V2
  Firmware Controllers.
*/

static inline void DAC960_V2_ClearCommand(DAC960_Command_T *Command)
{
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  memset(CommandMailbox, 0, sizeof(DAC960_V2_CommandMailbox_T));
  Command->V2.CommandStatus = 0;
}


/*
  DAC960_AllocateCommand allocates a Command structure from Controller's
  free list.  During driver initialization, a special initialization command
  has been placed on the free list to guarantee that command allocation can
  never fail.
*/

static inline DAC960_Command_T *DAC960_AllocateCommand(DAC960_Controller_T
						       *Controller)
{
  DAC960_Command_T *Command = Controller->FreeCommands;
  if (Command == NULL) return NULL;
  Controller->FreeCommands = Command->Next;
  Command->Next = NULL;
  return Command;
}


/*
  DAC960_DeallocateCommand deallocates Command, returning it to Controller's
  free list.
*/

static inline void DAC960_DeallocateCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;

  Command->Request = NULL;
  Command->Next = Controller->FreeCommands;
  Controller->FreeCommands = Command;
}


/*
  DAC960_WaitForCommand waits for a wake_up on Controller's Command Wait Queue.
*/

static void DAC960_WaitForCommand(DAC960_Controller_T *Controller)
{
  spin_unlock_irq(&Controller->queue_lock);
  __wait_event(Controller->CommandWaitQueue, Controller->FreeCommands);
  spin_lock_irq(&Controller->queue_lock);
}

/*
  DAC960_GEM_QueueCommand queues Command for DAC960 GEM Series Controllers.
*/

static void DAC960_GEM_QueueCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  DAC960_V2_CommandMailbox_T *NextCommandMailbox =
      Controller->V2.NextCommandMailbox;

  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
  DAC960_GEM_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);

  if (Controller->V2.PreviousCommandMailbox1->Words[0] == 0 ||
      Controller->V2.PreviousCommandMailbox2->Words[0] == 0)
      DAC960_GEM_MemoryMailboxNewCommand(ControllerBaseAddress);

  Controller->V2.PreviousCommandMailbox2 =
      Controller->V2.PreviousCommandMailbox1;
  Controller->V2.PreviousCommandMailbox1 = NextCommandMailbox;

  if (++NextCommandMailbox > Controller->V2.LastCommandMailbox)
      NextCommandMailbox = Controller->V2.FirstCommandMailbox;

  Controller->V2.NextCommandMailbox = NextCommandMailbox;
}

/*
  DAC960_BA_QueueCommand queues Command for DAC960 BA Series Controllers.
*/

static void DAC960_BA_QueueCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  DAC960_V2_CommandMailbox_T *NextCommandMailbox =
    Controller->V2.NextCommandMailbox;
  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
  DAC960_BA_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
  if (Controller->V2.PreviousCommandMailbox1->Words[0] == 0 ||
      Controller->V2.PreviousCommandMailbox2->Words[0] == 0)
    DAC960_BA_MemoryMailboxNewCommand(ControllerBaseAddress);
  Controller->V2.PreviousCommandMailbox2 =
    Controller->V2.PreviousCommandMailbox1;
  Controller->V2.PreviousCommandMailbox1 = NextCommandMailbox;
  if (++NextCommandMailbox > Controller->V2.LastCommandMailbox)
    NextCommandMailbox = Controller->V2.FirstCommandMailbox;
  Controller->V2.NextCommandMailbox = NextCommandMailbox;
}


/*
  DAC960_LP_QueueCommand queues Command for DAC960 LP Series Controllers.
*/

static void DAC960_LP_QueueCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  DAC960_V2_CommandMailbox_T *NextCommandMailbox =
    Controller->V2.NextCommandMailbox;
  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
  DAC960_LP_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
  if (Controller->V2.PreviousCommandMailbox1->Words[0] == 0 ||
      Controller->V2.PreviousCommandMailbox2->Words[0] == 0)
    DAC960_LP_MemoryMailboxNewCommand(ControllerBaseAddress);
  Controller->V2.PreviousCommandMailbox2 =
    Controller->V2.PreviousCommandMailbox1;
  Controller->V2.PreviousCommandMailbox1 = NextCommandMailbox;
  if (++NextCommandMailbox > Controller->V2.LastCommandMailbox)
    NextCommandMailbox = Controller->V2.FirstCommandMailbox;
  Controller->V2.NextCommandMailbox = NextCommandMailbox;
}


/*
  DAC960_LA_QueueCommandDualMode queues Command for DAC960 LA Series
  Controllers with Dual Mode Firmware.
*/

static void DAC960_LA_QueueCommandDualMode(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  DAC960_V1_CommandMailbox_T *NextCommandMailbox =
    Controller->V1.NextCommandMailbox;
  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
  DAC960_LA_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
  if (Controller->V1.PreviousCommandMailbox1->Words[0] == 0 ||
      Controller->V1.PreviousCommandMailbox2->Words[0] == 0)
    DAC960_LA_MemoryMailboxNewCommand(ControllerBaseAddress);
  Controller->V1.PreviousCommandMailbox2 =
    Controller->V1.PreviousCommandMailbox1;
  Controller->V1.PreviousCommandMailbox1 = NextCommandMailbox;
  if (++NextCommandMailbox > Controller->V1.LastCommandMailbox)
    NextCommandMailbox = Controller->V1.FirstCommandMailbox;
  Controller->V1.NextCommandMailbox = NextCommandMailbox;
}


/*
  DAC960_LA_QueueCommandSingleMode queues Command for DAC960 LA Series
  Controllers with Single Mode Firmware.
*/

static void DAC960_LA_QueueCommandSingleMode(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  DAC960_V1_CommandMailbox_T *NextCommandMailbox =
    Controller->V1.NextCommandMailbox;
  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
  DAC960_LA_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
  if (Controller->V1.PreviousCommandMailbox1->Words[0] == 0 ||
      Controller->V1.PreviousCommandMailbox2->Words[0] == 0)
    DAC960_LA_HardwareMailboxNewCommand(ControllerBaseAddress);
  Controller->V1.PreviousCommandMailbox2 =
    Controller->V1.PreviousCommandMailbox1;
  Controller->V1.PreviousCommandMailbox1 = NextCommandMailbox;
  if (++NextCommandMailbox > Controller->V1.LastCommandMailbox)
    NextCommandMailbox = Controller->V1.FirstCommandMailbox;
  Controller->V1.NextCommandMailbox = NextCommandMailbox;
}


/*
  DAC960_PG_QueueCommandDualMode queues Command for DAC960 PG Series
  Controllers with Dual Mode Firmware.
*/

static void DAC960_PG_QueueCommandDualMode(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  DAC960_V1_CommandMailbox_T *NextCommandMailbox =
    Controller->V1.NextCommandMailbox;
  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
  DAC960_PG_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
  if (Controller->V1.PreviousCommandMailbox1->Words[0] == 0 ||
      Controller->V1.PreviousCommandMailbox2->Words[0] == 0)
    DAC960_PG_MemoryMailboxNewCommand(ControllerBaseAddress);
  Controller->V1.PreviousCommandMailbox2 =
    Controller->V1.PreviousCommandMailbox1;
  Controller->V1.PreviousCommandMailbox1 = NextCommandMailbox;
  if (++NextCommandMailbox > Controller->V1.LastCommandMailbox)
    NextCommandMailbox = Controller->V1.FirstCommandMailbox;
  Controller->V1.NextCommandMailbox = NextCommandMailbox;
}


/*
  DAC960_PG_QueueCommandSingleMode queues Command for DAC960 PG Series
  Controllers with Single Mode Firmware.
*/

static void DAC960_PG_QueueCommandSingleMode(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  DAC960_V1_CommandMailbox_T *NextCommandMailbox =
    Controller->V1.NextCommandMailbox;
  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
  DAC960_PG_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
  if (Controller->V1.PreviousCommandMailbox1->Words[0] == 0 ||
      Controller->V1.PreviousCommandMailbox2->Words[0] == 0)
    DAC960_PG_HardwareMailboxNewCommand(ControllerBaseAddress);
  Controller->V1.PreviousCommandMailbox2 =
    Controller->V1.PreviousCommandMailbox1;
  Controller->V1.PreviousCommandMailbox1 = NextCommandMailbox;
  if (++NextCommandMailbox > Controller->V1.LastCommandMailbox)
    NextCommandMailbox = Controller->V1.FirstCommandMailbox;
  Controller->V1.NextCommandMailbox = NextCommandMailbox;
}


/*
  DAC960_PD_QueueCommand queues Command for DAC960 PD Series Controllers.
*/

static void DAC960_PD_QueueCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
  while (DAC960_PD_MailboxFullP(ControllerBaseAddress))
    udelay(1);
  DAC960_PD_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox);
  DAC960_PD_NewCommand(ControllerBaseAddress);
}


/*
  DAC960_P_QueueCommand queues Command for DAC960 P Series Controllers.
*/

static void DAC960_P_QueueCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
  switch (CommandMailbox->Common.CommandOpcode)
    {
    case DAC960_V1_Enquiry:
      CommandMailbox->Common.CommandOpcode = DAC960_V1_Enquiry_Old;
      break;
    case DAC960_V1_GetDeviceState:
      CommandMailbox->Common.CommandOpcode = DAC960_V1_GetDeviceState_Old;
      break;
    case DAC960_V1_Read:
      CommandMailbox->Common.CommandOpcode = DAC960_V1_Read_Old;
      DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
      break;
    case DAC960_V1_Write:
      CommandMailbox->Common.CommandOpcode = DAC960_V1_Write_Old;
      DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
      break;
    case DAC960_V1_ReadWithScatterGather:
      CommandMailbox->Common.CommandOpcode =
	DAC960_V1_ReadWithScatterGather_Old;
      DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
      break;
    case DAC960_V1_WriteWithScatterGather:
      CommandMailbox->Common.CommandOpcode =
	DAC960_V1_WriteWithScatterGather_Old;
      DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
      break;
    default:
      break;
    }
  while (DAC960_PD_MailboxFullP(ControllerBaseAddress))
    udelay(1);
  DAC960_PD_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox);
  DAC960_PD_NewCommand(ControllerBaseAddress);
}


/*
  DAC960_ExecuteCommand executes Command and waits for completion.
*/

static void DAC960_ExecuteCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  DECLARE_COMPLETION_ONSTACK(Completion);
  unsigned long flags;
  Command->Completion = &Completion;

  spin_lock_irqsave(&Controller->queue_lock, flags);
  DAC960_QueueCommand(Command);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
 
  if (in_interrupt())
	  return;
  wait_for_completion(&Completion);
}


/*
  DAC960_V1_ExecuteType3 executes a DAC960 V1 Firmware Controller Type 3
  Command and waits for completion.  It returns true on success and false
  on failure.
*/

static bool DAC960_V1_ExecuteType3(DAC960_Controller_T *Controller,
				      DAC960_V1_CommandOpcode_T CommandOpcode,
				      dma_addr_t DataDMA)
{
  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  DAC960_V1_CommandStatus_T CommandStatus;
  DAC960_V1_ClearCommand(Command);
  Command->CommandType = DAC960_ImmediateCommand;
  CommandMailbox->Type3.CommandOpcode = CommandOpcode;
  CommandMailbox->Type3.BusAddress = DataDMA;
  DAC960_ExecuteCommand(Command);
  CommandStatus = Command->V1.CommandStatus;
  DAC960_DeallocateCommand(Command);
  return (CommandStatus == DAC960_V1_NormalCompletion);
}


/*
  DAC960_V1_ExecuteTypeB executes a DAC960 V1 Firmware Controller Type 3B
  Command and waits for completion.  It returns true on success and false
  on failure.
*/

static bool DAC960_V1_ExecuteType3B(DAC960_Controller_T *Controller,
				       DAC960_V1_CommandOpcode_T CommandOpcode,
				       unsigned char CommandOpcode2,
				       dma_addr_t DataDMA)
{
  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  DAC960_V1_CommandStatus_T CommandStatus;
  DAC960_V1_ClearCommand(Command);
  Command->CommandType = DAC960_ImmediateCommand;
  CommandMailbox->Type3B.CommandOpcode = CommandOpcode;
  CommandMailbox->Type3B.CommandOpcode2 = CommandOpcode2;
  CommandMailbox->Type3B.BusAddress = DataDMA;
  DAC960_ExecuteCommand(Command);
  CommandStatus = Command->V1.CommandStatus;
  DAC960_DeallocateCommand(Command);
  return (CommandStatus == DAC960_V1_NormalCompletion);
}


/*
  DAC960_V1_ExecuteType3D executes a DAC960 V1 Firmware Controller Type 3D
  Command and waits for completion.  It returns true on success and false
  on failure.
*/

static bool DAC960_V1_ExecuteType3D(DAC960_Controller_T *Controller,
				       DAC960_V1_CommandOpcode_T CommandOpcode,
				       unsigned char Channel,
				       unsigned char TargetID,
				       dma_addr_t DataDMA)
{
  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  DAC960_V1_CommandStatus_T CommandStatus;
  DAC960_V1_ClearCommand(Command);
  Command->CommandType = DAC960_ImmediateCommand;
  CommandMailbox->Type3D.CommandOpcode = CommandOpcode;
  CommandMailbox->Type3D.Channel = Channel;
  CommandMailbox->Type3D.TargetID = TargetID;
  CommandMailbox->Type3D.BusAddress = DataDMA;
  DAC960_ExecuteCommand(Command);
  CommandStatus = Command->V1.CommandStatus;
  DAC960_DeallocateCommand(Command);
  return (CommandStatus == DAC960_V1_NormalCompletion);
}


/*
  DAC960_V2_GeneralInfo executes a DAC960 V2 Firmware General Information
  Reading IOCTL Command and waits for completion.  It returns true on success
  and false on failure.

  Return data in The controller's HealthStatusBuffer, which is dma-able memory
*/

static bool DAC960_V2_GeneralInfo(DAC960_Controller_T *Controller)
{
  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  DAC960_V2_CommandStatus_T CommandStatus;
  DAC960_V2_ClearCommand(Command);
  Command->CommandType = DAC960_ImmediateCommand;
  CommandMailbox->Common.CommandOpcode = DAC960_V2_IOCTL;
  CommandMailbox->Common.CommandControlBits
			.DataTransferControllerToHost = true;
  CommandMailbox->Common.CommandControlBits
			.NoAutoRequestSense = true;
  CommandMailbox->Common.DataTransferSize = sizeof(DAC960_V2_HealthStatusBuffer_T);
  CommandMailbox->Common.IOCTL_Opcode = DAC960_V2_GetHealthStatus;
  CommandMailbox->Common.DataTransferMemoryAddress
			.ScatterGatherSegments[0]
			.SegmentDataPointer =
    Controller->V2.HealthStatusBufferDMA;
  CommandMailbox->Common.DataTransferMemoryAddress
			.ScatterGatherSegments[0]
			.SegmentByteCount =
    CommandMailbox->Common.DataTransferSize;
  DAC960_ExecuteCommand(Command);
  CommandStatus = Command->V2.CommandStatus;
  DAC960_DeallocateCommand(Command);
  return (CommandStatus == DAC960_V2_NormalCompletion);
}


/*
  DAC960_V2_ControllerInfo executes a DAC960 V2 Firmware Controller
  Information Reading IOCTL Command and waits for completion.  It returns
  true on success and false on failure.

  Data is returned in the controller's V2.NewControllerInformation dma-able
  memory buffer.
*/

static bool DAC960_V2_NewControllerInfo(DAC960_Controller_T *Controller)
{
  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  DAC960_V2_CommandStatus_T CommandStatus;
  DAC960_V2_ClearCommand(Command);
  Command->CommandType = DAC960_ImmediateCommand;
  CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL;
  CommandMailbox->ControllerInfo.CommandControlBits
				.DataTransferControllerToHost = true;
  CommandMailbox->ControllerInfo.CommandControlBits
				.NoAutoRequestSense = true;
  CommandMailbox->ControllerInfo.DataTransferSize = sizeof(DAC960_V2_ControllerInfo_T);
  CommandMailbox->ControllerInfo.ControllerNumber = 0;
  CommandMailbox->ControllerInfo.IOCTL_Opcode = DAC960_V2_GetControllerInfo;
  CommandMailbox->ControllerInfo.DataTransferMemoryAddress
				.ScatterGatherSegments[0]
				.SegmentDataPointer =
    	Controller->V2.NewControllerInformationDMA;
  CommandMailbox->ControllerInfo.DataTransferMemoryAddress
				.ScatterGatherSegments[0]
				.SegmentByteCount =
    CommandMailbox->ControllerInfo.DataTransferSize;
  DAC960_ExecuteCommand(Command);
  CommandStatus = Command->V2.CommandStatus;
  DAC960_DeallocateCommand(Command);
  return (CommandStatus == DAC960_V2_NormalCompletion);
}


/*
  DAC960_V2_LogicalDeviceInfo executes a DAC960 V2 Firmware Controller Logical
  Device Information Reading IOCTL Command and waits for completion.  It
  returns true on success and false on failure.

  Data is returned in the controller's V2.NewLogicalDeviceInformation
*/

static bool DAC960_V2_NewLogicalDeviceInfo(DAC960_Controller_T *Controller,
					   unsigned short LogicalDeviceNumber)
{
  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  DAC960_V2_CommandStatus_T CommandStatus;

  DAC960_V2_ClearCommand(Command);
  Command->CommandType = DAC960_ImmediateCommand;
  CommandMailbox->LogicalDeviceInfo.CommandOpcode =
				DAC960_V2_IOCTL;
  CommandMailbox->LogicalDeviceInfo.CommandControlBits
				   .DataTransferControllerToHost = true;
  CommandMailbox->LogicalDeviceInfo.CommandControlBits
				   .NoAutoRequestSense = true;
  CommandMailbox->LogicalDeviceInfo.DataTransferSize = 
				sizeof(DAC960_V2_LogicalDeviceInfo_T);
  CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber =
    LogicalDeviceNumber;
  CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode = DAC960_V2_GetLogicalDeviceInfoValid;
  CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress
				   .ScatterGatherSegments[0]
				   .SegmentDataPointer =
    	Controller->V2.NewLogicalDeviceInformationDMA;
  CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress
				   .ScatterGatherSegments[0]
				   .SegmentByteCount =
    CommandMailbox->LogicalDeviceInfo.DataTransferSize;
  DAC960_ExecuteCommand(Command);
  CommandStatus = Command->V2.CommandStatus;
  DAC960_DeallocateCommand(Command);
  return (CommandStatus == DAC960_V2_NormalCompletion);
}


/*
  DAC960_V2_PhysicalDeviceInfo executes a DAC960 V2 Firmware Controller "Read
  Physical Device Information" IOCTL Command and waits for completion.  It
  returns true on success and false on failure.

  The Channel, TargetID, LogicalUnit arguments should be 0 the first time
  this function is called for a given controller.  This will return data
  for the "first" device on that controller.  The returned data includes a
  Channel, TargetID, LogicalUnit that can be passed in to this routine to
  get data for the NEXT device on that controller.

  Data is stored in the controller's V2.NewPhysicalDeviceInfo dma-able
  memory buffer.

*/

static bool DAC960_V2_NewPhysicalDeviceInfo(DAC960_Controller_T *Controller,
					    unsigned char Channel,
					    unsigned char TargetID,
					    unsigned char LogicalUnit)
{
  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  DAC960_V2_CommandStatus_T CommandStatus;

  DAC960_V2_ClearCommand(Command);
  Command->CommandType = DAC960_ImmediateCommand;
  CommandMailbox->PhysicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL;
  CommandMailbox->PhysicalDeviceInfo.CommandControlBits
				    .DataTransferControllerToHost = true;
  CommandMailbox->PhysicalDeviceInfo.CommandControlBits
				    .NoAutoRequestSense = true;
  CommandMailbox->PhysicalDeviceInfo.DataTransferSize =
				sizeof(DAC960_V2_PhysicalDeviceInfo_T);
  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.LogicalUnit = LogicalUnit;
  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.TargetID = TargetID;
  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.Channel = Channel;
  CommandMailbox->PhysicalDeviceInfo.IOCTL_Opcode =
					DAC960_V2_GetPhysicalDeviceInfoValid;
  CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress
				    .ScatterGatherSegments[0]
				    .SegmentDataPointer =
    					Controller->V2.NewPhysicalDeviceInformationDMA;
  CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress
				    .ScatterGatherSegments[0]
				    .SegmentByteCount =
    CommandMailbox->PhysicalDeviceInfo.DataTransferSize;
  DAC960_ExecuteCommand(Command);
  CommandStatus = Command->V2.CommandStatus;
  DAC960_DeallocateCommand(Command);
  return (CommandStatus == DAC960_V2_NormalCompletion);
}


static void DAC960_V2_ConstructNewUnitSerialNumber(
	DAC960_Controller_T *Controller,
	DAC960_V2_CommandMailbox_T *CommandMailbox, int Channel, int TargetID,
	int LogicalUnit)
{
      CommandMailbox->SCSI_10.CommandOpcode = DAC960_V2_SCSI_10_Passthru;
      CommandMailbox->SCSI_10.CommandControlBits
			     .DataTransferControllerToHost = true;
      CommandMailbox->SCSI_10.CommandControlBits
			     .NoAutoRequestSense = true;
      CommandMailbox->SCSI_10.DataTransferSize =
	sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
      CommandMailbox->SCSI_10.PhysicalDevice.LogicalUnit = LogicalUnit;
      CommandMailbox->SCSI_10.PhysicalDevice.TargetID = TargetID;
      CommandMailbox->SCSI_10.PhysicalDevice.Channel = Channel;
      CommandMailbox->SCSI_10.CDBLength = 6;
      CommandMailbox->SCSI_10.SCSI_CDB[0] = 0x12; /* INQUIRY */
      CommandMailbox->SCSI_10.SCSI_CDB[1] = 1; /* EVPD = 1 */
      CommandMailbox->SCSI_10.SCSI_CDB[2] = 0x80; /* Page Code */
      CommandMailbox->SCSI_10.SCSI_CDB[3] = 0; /* Reserved */
      CommandMailbox->SCSI_10.SCSI_CDB[4] =
	sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
      CommandMailbox->SCSI_10.SCSI_CDB[5] = 0; /* Control */
      CommandMailbox->SCSI_10.DataTransferMemoryAddress
			     .ScatterGatherSegments[0]
			     .SegmentDataPointer =
		Controller->V2.NewInquiryUnitSerialNumberDMA;
      CommandMailbox->SCSI_10.DataTransferMemoryAddress
			     .ScatterGatherSegments[0]
			     .SegmentByteCount =
		CommandMailbox->SCSI_10.DataTransferSize;
}


/*
  DAC960_V2_NewUnitSerialNumber executes an SCSI pass-through
  Inquiry command to a SCSI device identified by Channel number,
  Target id, Logical Unit Number.  This function Waits for completion
  of the command.

  The return data includes Unit Serial Number information for the
  specified device.

  Data is stored in the controller's V2.NewPhysicalDeviceInfo dma-able
  memory buffer.
*/

static bool DAC960_V2_NewInquiryUnitSerialNumber(DAC960_Controller_T *Controller,
			int Channel, int TargetID, int LogicalUnit)
{
      DAC960_Command_T *Command;
      DAC960_V2_CommandMailbox_T *CommandMailbox;
      DAC960_V2_CommandStatus_T CommandStatus;

      Command = DAC960_AllocateCommand(Controller);
      CommandMailbox = &Command->V2.CommandMailbox;
      DAC960_V2_ClearCommand(Command);
      Command->CommandType = DAC960_ImmediateCommand;

      DAC960_V2_ConstructNewUnitSerialNumber(Controller, CommandMailbox,
			Channel, TargetID, LogicalUnit);

      DAC960_ExecuteCommand(Command);
      CommandStatus = Command->V2.CommandStatus;
      DAC960_DeallocateCommand(Command);
      return (CommandStatus == DAC960_V2_NormalCompletion);
}


/*
  DAC960_V2_DeviceOperation executes a DAC960 V2 Firmware Controller Device
  Operation IOCTL Command and waits for completion.  It returns true on
  success and false on failure.
*/

static bool DAC960_V2_DeviceOperation(DAC960_Controller_T *Controller,
					 DAC960_V2_IOCTL_Opcode_T IOCTL_Opcode,
					 DAC960_V2_OperationDevice_T
					   OperationDevice)
{
  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  DAC960_V2_CommandStatus_T CommandStatus;
  DAC960_V2_ClearCommand(Command);
  Command->CommandType = DAC960_ImmediateCommand;
  CommandMailbox->DeviceOperation.CommandOpcode = DAC960_V2_IOCTL;
  CommandMailbox->DeviceOperation.CommandControlBits
				 .DataTransferControllerToHost = true;
  CommandMailbox->DeviceOperation.CommandControlBits
    				 .NoAutoRequestSense = true;
  CommandMailbox->DeviceOperation.IOCTL_Opcode = IOCTL_Opcode;
  CommandMailbox->DeviceOperation.OperationDevice = OperationDevice;
  DAC960_ExecuteCommand(Command);
  CommandStatus = Command->V2.CommandStatus;
  DAC960_DeallocateCommand(Command);
  return (CommandStatus == DAC960_V2_NormalCompletion);
}


/*
  DAC960_V1_EnableMemoryMailboxInterface enables the Memory Mailbox Interface
  for DAC960 V1 Firmware Controllers.

  PD and P controller types have no memory mailbox, but still need the
  other dma mapped memory.
*/

static bool DAC960_V1_EnableMemoryMailboxInterface(DAC960_Controller_T
						      *Controller)
{
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_HardwareType_T hw_type = Controller->HardwareType;
  struct pci_dev *PCI_Device = Controller->PCIDevice;
  struct dma_loaf *DmaPages = &Controller->DmaPages;
  size_t DmaPagesSize;
  size_t CommandMailboxesSize;
  size_t StatusMailboxesSize;

  DAC960_V1_CommandMailbox_T *CommandMailboxesMemory;
  dma_addr_t CommandMailboxesMemoryDMA;

  DAC960_V1_StatusMailbox_T *StatusMailboxesMemory;
  dma_addr_t StatusMailboxesMemoryDMA;

  DAC960_V1_CommandMailbox_T CommandMailbox;
  DAC960_V1_CommandStatus_T CommandStatus;
  int TimeoutCounter;
  int i;

  
  if (pci_set_dma_mask(Controller->PCIDevice, DMA_32BIT_MASK))
	return DAC960_Failure(Controller, "DMA mask out of range");
  Controller->BounceBufferLimit = DMA_32BIT_MASK;

  if ((hw_type == DAC960_PD_Controller) || (hw_type == DAC960_P_Controller)) {
    CommandMailboxesSize =  0;
    StatusMailboxesSize = 0;
  } else {
    CommandMailboxesSize =  DAC960_V1_CommandMailboxCount * sizeof(DAC960_V1_CommandMailbox_T);
    StatusMailboxesSize = DAC960_V1_StatusMailboxCount * sizeof(DAC960_V1_StatusMailbox_T);
  }
  DmaPagesSize = CommandMailboxesSize + StatusMailboxesSize + 
	sizeof(DAC960_V1_DCDB_T) + sizeof(DAC960_V1_Enquiry_T) +
	sizeof(DAC960_V1_ErrorTable_T) + sizeof(DAC960_V1_EventLogEntry_T) +
	sizeof(DAC960_V1_RebuildProgress_T) +
	sizeof(DAC960_V1_LogicalDriveInformationArray_T) +
	sizeof(DAC960_V1_BackgroundInitializationStatus_T) +
	sizeof(DAC960_V1_DeviceState_T) + sizeof(DAC960_SCSI_Inquiry_T) +
	sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);

  if (!init_dma_loaf(PCI_Device, DmaPages, DmaPagesSize))
	return false;


  if ((hw_type == DAC960_PD_Controller) || (hw_type == DAC960_P_Controller)) 
	goto skip_mailboxes;

  CommandMailboxesMemory = slice_dma_loaf(DmaPages,
                CommandMailboxesSize, &CommandMailboxesMemoryDMA);
  
  /* These are the base addresses for the command memory mailbox array */
  Controller->V1.FirstCommandMailbox = CommandMailboxesMemory;
  Controller->V1.FirstCommandMailboxDMA = CommandMailboxesMemoryDMA;

  CommandMailboxesMemory += DAC960_V1_CommandMailboxCount - 1;
  Controller->V1.LastCommandMailbox = CommandMailboxesMemory;
  Controller->V1.NextCommandMailbox = Controller->V1.FirstCommandMailbox;
  Controller->V1.PreviousCommandMailbox1 = Controller->V1.LastCommandMailbox;
  Controller->V1.PreviousCommandMailbox2 =
	  				Controller->V1.LastCommandMailbox - 1;

  /* These are the base addresses for the status memory mailbox array */
  StatusMailboxesMemory = slice_dma_loaf(DmaPages,
                StatusMailboxesSize, &StatusMailboxesMemoryDMA);

  Controller->V1.FirstStatusMailbox = StatusMailboxesMemory;
  Controller->V1.FirstStatusMailboxDMA = StatusMailboxesMemoryDMA;
  StatusMailboxesMemory += DAC960_V1_StatusMailboxCount - 1;
  Controller->V1.LastStatusMailbox = StatusMailboxesMemory;
  Controller->V1.NextStatusMailbox = Controller->V1.FirstStatusMailbox;

skip_mailboxes:
  Controller->V1.MonitoringDCDB = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V1_DCDB_T),
                &Controller->V1.MonitoringDCDB_DMA);

  Controller->V1.NewEnquiry = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V1_Enquiry_T),
                &Controller->V1.NewEnquiryDMA);

  Controller->V1.NewErrorTable = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V1_ErrorTable_T),
                &Controller->V1.NewErrorTableDMA);

  Controller->V1.EventLogEntry = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V1_EventLogEntry_T),
                &Controller->V1.EventLogEntryDMA);

  Controller->V1.RebuildProgress = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V1_RebuildProgress_T),
                &Controller->V1.RebuildProgressDMA);

  Controller->V1.NewLogicalDriveInformation = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V1_LogicalDriveInformationArray_T),
                &Controller->V1.NewLogicalDriveInformationDMA);

  Controller->V1.BackgroundInitializationStatus = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V1_BackgroundInitializationStatus_T),
                &Controller->V1.BackgroundInitializationStatusDMA);

  Controller->V1.NewDeviceState = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V1_DeviceState_T),
                &Controller->V1.NewDeviceStateDMA);

  Controller->V1.NewInquiryStandardData = slice_dma_loaf(DmaPages,
                sizeof(DAC960_SCSI_Inquiry_T),
                &Controller->V1.NewInquiryStandardDataDMA);

  Controller->V1.NewInquiryUnitSerialNumber = slice_dma_loaf(DmaPages,
                sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T),
                &Controller->V1.NewInquiryUnitSerialNumberDMA);

  if ((hw_type == DAC960_PD_Controller) || (hw_type == DAC960_P_Controller))
	return true;
 
  /* Enable the Memory Mailbox Interface. */
  Controller->V1.DualModeMemoryMailboxInterface = true;
  CommandMailbox.TypeX.CommandOpcode = 0x2B;
  CommandMailbox.TypeX.CommandIdentifier = 0;
  CommandMailbox.TypeX.CommandOpcode2 = 0x14;
  CommandMailbox.TypeX.CommandMailboxesBusAddress =
    				Controller->V1.FirstCommandMailboxDMA;
  CommandMailbox.TypeX.StatusMailboxesBusAddress =
    				Controller->V1.FirstStatusMailboxDMA;
#define TIMEOUT_COUNT 1000000

  for (i = 0; i < 2; i++)
    switch (Controller->HardwareType)
      {
      case DAC960_LA_Controller:
	TimeoutCounter = TIMEOUT_COUNT;
	while (--TimeoutCounter >= 0)
	  {
	    if (!DAC960_LA_HardwareMailboxFullP(ControllerBaseAddress))
	      break;
	    udelay(10);
	  }
	if (TimeoutCounter < 0) return false;
	DAC960_LA_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox);
	DAC960_LA_HardwareMailboxNewCommand(ControllerBaseAddress);
	TimeoutCounter = TIMEOUT_COUNT;
	while (--TimeoutCounter >= 0)
	  {
	    if (DAC960_LA_HardwareMailboxStatusAvailableP(
		  ControllerBaseAddress))
	      break;
	    udelay(10);
	  }
	if (TimeoutCounter < 0) return false;
	CommandStatus = DAC960_LA_ReadStatusRegister(ControllerBaseAddress);
	DAC960_LA_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
	DAC960_LA_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
	if (CommandStatus == DAC960_V1_NormalCompletion) return true;
	Controller->V1.DualModeMemoryMailboxInterface = false;
	CommandMailbox.TypeX.CommandOpcode2 = 0x10;
	break;
      case DAC960_PG_Controller:
	TimeoutCounter = TIMEOUT_COUNT;
	while (--TimeoutCounter >= 0)
	  {
	    if (!DAC960_PG_HardwareMailboxFullP(ControllerBaseAddress))
	      break;
	    udelay(10);
	  }
	if (TimeoutCounter < 0) return false;
	DAC960_PG_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox);
	DAC960_PG_HardwareMailboxNewCommand(ControllerBaseAddress);

	TimeoutCounter = TIMEOUT_COUNT;
	while (--TimeoutCounter >= 0)
	  {
	    if (DAC960_PG_HardwareMailboxStatusAvailableP(
		  ControllerBaseAddress))
	      break;
	    udelay(10);
	  }
	if (TimeoutCounter < 0) return false;
	CommandStatus = DAC960_PG_ReadStatusRegister(ControllerBaseAddress);
	DAC960_PG_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
	DAC960_PG_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
	if (CommandStatus == DAC960_V1_NormalCompletion) return true;
	Controller->V1.DualModeMemoryMailboxInterface = false;
	CommandMailbox.TypeX.CommandOpcode2 = 0x10;
	break;
      default:
        DAC960_Failure(Controller, "Unknown Controller Type\n");
	break;
      }
  return false;
}


/*
  DAC960_V2_EnableMemoryMailboxInterface enables the Memory Mailbox Interface
  for DAC960 V2 Firmware Controllers.

  Aggregate the space needed for the controller's memory mailbox and
  the other data structures that will be targets of dma transfers with
  the controller.  Allocate a dma-mapped region of memory to hold these
  structures.  Then, save CPU pointers and dma_addr_t values to reference
  the structures that are contained in that region.
*/

static bool DAC960_V2_EnableMemoryMailboxInterface(DAC960_Controller_T
						      *Controller)
{
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  struct pci_dev *PCI_Device = Controller->PCIDevice;
  struct dma_loaf *DmaPages = &Controller->DmaPages;
  size_t DmaPagesSize;
  size_t CommandMailboxesSize;
  size_t StatusMailboxesSize;

  DAC960_V2_CommandMailbox_T *CommandMailboxesMemory;
  dma_addr_t CommandMailboxesMemoryDMA;

  DAC960_V2_StatusMailbox_T *StatusMailboxesMemory;
  dma_addr_t StatusMailboxesMemoryDMA;

  DAC960_V2_CommandMailbox_T *CommandMailbox;
  dma_addr_t	CommandMailboxDMA;
  DAC960_V2_CommandStatus_T CommandStatus;

	if (!pci_set_dma_mask(Controller->PCIDevice, DMA_64BIT_MASK))
		Controller->BounceBufferLimit = DMA_64BIT_MASK;
	else if (!pci_set_dma_mask(Controller->PCIDevice, DMA_32BIT_MASK))
		Controller->BounceBufferLimit = DMA_32BIT_MASK;
	else
		return DAC960_Failure(Controller, "DMA mask out of range");

  /* This is a temporary dma mapping, used only in the scope of this function */
  CommandMailbox = pci_alloc_consistent(PCI_Device,
		sizeof(DAC960_V2_CommandMailbox_T), &CommandMailboxDMA);
  if (CommandMailbox == NULL)
	  return false;

  CommandMailboxesSize = DAC960_V2_CommandMailboxCount * sizeof(DAC960_V2_CommandMailbox_T);
  StatusMailboxesSize = DAC960_V2_StatusMailboxCount * sizeof(DAC960_V2_StatusMailbox_T);
  DmaPagesSize =
    CommandMailboxesSize + StatusMailboxesSize +
    sizeof(DAC960_V2_HealthStatusBuffer_T) +
    sizeof(DAC960_V2_ControllerInfo_T) +
    sizeof(DAC960_V2_LogicalDeviceInfo_T) +
    sizeof(DAC960_V2_PhysicalDeviceInfo_T) +
    sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T) +
    sizeof(DAC960_V2_Event_T) +
    sizeof(DAC960_V2_PhysicalToLogicalDevice_T);

  if (!init_dma_loaf(PCI_Device, DmaPages, DmaPagesSize)) {
  	pci_free_consistent(PCI_Device, sizeof(DAC960_V2_CommandMailbox_T),
					CommandMailbox, CommandMailboxDMA);
	return false;
  }

  CommandMailboxesMemory = slice_dma_loaf(DmaPages,
		CommandMailboxesSize, &CommandMailboxesMemoryDMA);

  /* These are the base addresses for the command memory mailbox array */
  Controller->V2.FirstCommandMailbox = CommandMailboxesMemory;
  Controller->V2.FirstCommandMailboxDMA = CommandMailboxesMemoryDMA;

  CommandMailboxesMemory += DAC960_V2_CommandMailboxCount - 1;
  Controller->V2.LastCommandMailbox = CommandMailboxesMemory;
  Controller->V2.NextCommandMailbox = Controller->V2.FirstCommandMailbox;
  Controller->V2.PreviousCommandMailbox1 = Controller->V2.LastCommandMailbox;
  Controller->V2.PreviousCommandMailbox2 =
    					Controller->V2.LastCommandMailbox - 1;

  /* These are the base addresses for the status memory mailbox array */
  StatusMailboxesMemory = slice_dma_loaf(DmaPages,
		StatusMailboxesSize, &StatusMailboxesMemoryDMA);

  Controller->V2.FirstStatusMailbox = StatusMailboxesMemory;
  Controller->V2.FirstStatusMailboxDMA = StatusMailboxesMemoryDMA;
  StatusMailboxesMemory += DAC960_V2_StatusMailboxCount - 1;
  Controller->V2.LastStatusMailbox = StatusMailboxesMemory;
  Controller->V2.NextStatusMailbox = Controller->V2.FirstStatusMailbox;

  Controller->V2.HealthStatusBuffer = slice_dma_loaf(DmaPages,
		sizeof(DAC960_V2_HealthStatusBuffer_T),
		&Controller->V2.HealthStatusBufferDMA);

  Controller->V2.NewControllerInformation = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V2_ControllerInfo_T), 
                &Controller->V2.NewControllerInformationDMA);

  Controller->V2.NewLogicalDeviceInformation =  slice_dma_loaf(DmaPages,
                sizeof(DAC960_V2_LogicalDeviceInfo_T),
                &Controller->V2.NewLogicalDeviceInformationDMA);

  Controller->V2.NewPhysicalDeviceInformation = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V2_PhysicalDeviceInfo_T),
                &Controller->V2.NewPhysicalDeviceInformationDMA);

  Controller->V2.NewInquiryUnitSerialNumber = slice_dma_loaf(DmaPages,
                sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T),
                &Controller->V2.NewInquiryUnitSerialNumberDMA);

  Controller->V2.Event = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V2_Event_T),
                &Controller->V2.EventDMA);

  Controller->V2.PhysicalToLogicalDevice = slice_dma_loaf(DmaPages,
                sizeof(DAC960_V2_PhysicalToLogicalDevice_T),
                &Controller->V2.PhysicalToLogicalDeviceDMA);

  /*
    Enable the Memory Mailbox Interface.
    
    I don't know why we can't just use one of the memory mailboxes
    we just allocated to do this, instead of using this temporary one.
    Try this change later.
  */
  memset(CommandMailbox, 0, sizeof(DAC960_V2_CommandMailbox_T));
  CommandMailbox->SetMemoryMailbox.CommandIdentifier = 1;
  CommandMailbox->SetMemoryMailbox.CommandOpcode = DAC960_V2_IOCTL;
  CommandMailbox->SetMemoryMailbox.CommandControlBits.NoAutoRequestSense = true;
  CommandMailbox->SetMemoryMailbox.FirstCommandMailboxSizeKB =
    (DAC960_V2_CommandMailboxCount * sizeof(DAC960_V2_CommandMailbox_T)) >> 10;
  CommandMailbox->SetMemoryMailbox.FirstStatusMailboxSizeKB =
    (DAC960_V2_StatusMailboxCount * sizeof(DAC960_V2_StatusMailbox_T)) >> 10;
  CommandMailbox->SetMemoryMailbox.SecondCommandMailboxSizeKB = 0;
  CommandMailbox->SetMemoryMailbox.SecondStatusMailboxSizeKB = 0;
  CommandMailbox->SetMemoryMailbox.RequestSenseSize = 0;
  CommandMailbox->SetMemoryMailbox.IOCTL_Opcode = DAC960_V2_SetMemoryMailbox;
  CommandMailbox->SetMemoryMailbox.HealthStatusBufferSizeKB = 1;
  CommandMailbox->SetMemoryMailbox.HealthStatusBufferBusAddress =
    					Controller->V2.HealthStatusBufferDMA;
  CommandMailbox->SetMemoryMailbox.FirstCommandMailboxBusAddress =
    					Controller->V2.FirstCommandMailboxDMA;
  CommandMailbox->SetMemoryMailbox.FirstStatusMailboxBusAddress =
    					Controller->V2.FirstStatusMailboxDMA;
  switch (Controller->HardwareType)
    {
    case DAC960_GEM_Controller:
      while (DAC960_GEM_HardwareMailboxFullP(ControllerBaseAddress))
	udelay(1);
      DAC960_GEM_WriteHardwareMailbox(ControllerBaseAddress, CommandMailboxDMA);
      DAC960_GEM_HardwareMailboxNewCommand(ControllerBaseAddress);
      while (!DAC960_GEM_HardwareMailboxStatusAvailableP(ControllerBaseAddress))
	udelay(1);
      CommandStatus = DAC960_GEM_ReadCommandStatus(ControllerBaseAddress);
      DAC960_GEM_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
      DAC960_GEM_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
      break;
    case DAC960_BA_Controller:
      while (DAC960_BA_HardwareMailboxFullP(ControllerBaseAddress))
	udelay(1);
      DAC960_BA_WriteHardwareMailbox(ControllerBaseAddress, CommandMailboxDMA);
      DAC960_BA_HardwareMailboxNewCommand(ControllerBaseAddress);
      while (!DAC960_BA_HardwareMailboxStatusAvailableP(ControllerBaseAddress))
	udelay(1);
      CommandStatus = DAC960_BA_ReadCommandStatus(ControllerBaseAddress);
      DAC960_BA_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
      DAC960_BA_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
      break;
    case DAC960_LP_Controller:
      while (DAC960_LP_HardwareMailboxFullP(ControllerBaseAddress))
	udelay(1);
      DAC960_LP_WriteHardwareMailbox(ControllerBaseAddress, CommandMailboxDMA);
      DAC960_LP_HardwareMailboxNewCommand(ControllerBaseAddress);
      while (!DAC960_LP_HardwareMailboxStatusAvailableP(ControllerBaseAddress))
	udelay(1);
      CommandStatus = DAC960_LP_ReadCommandStatus(ControllerBaseAddress);
      DAC960_LP_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
      DAC960_LP_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
      break;
    default:
      DAC960_Failure(Controller, "Unknown Controller Type\n");
      CommandStatus = DAC960_V2_AbormalCompletion;
      break;
    }
  pci_free_consistent(PCI_Device, sizeof(DAC960_V2_CommandMailbox_T),
					CommandMailbox, CommandMailboxDMA);
  return (CommandStatus == DAC960_V2_NormalCompletion);
}


/*
  DAC960_V1_ReadControllerConfiguration reads the Configuration Information
  from DAC960 V1 Firmware Controllers and initializes the Controller structure.
*/

static bool DAC960_V1_ReadControllerConfiguration(DAC960_Controller_T
						     *Controller)
{
  DAC960_V1_Enquiry2_T *Enquiry2;
  dma_addr_t Enquiry2DMA;
  DAC960_V1_Config2_T *Config2;
  dma_addr_t Config2DMA;
  int LogicalDriveNumber, Channel, TargetID;
  struct dma_loaf local_dma;

  if (!init_dma_loaf(Controller->PCIDevice, &local_dma,
		sizeof(DAC960_V1_Enquiry2_T) + sizeof(DAC960_V1_Config2_T)))
	return DAC960_Failure(Controller, "LOGICAL DEVICE ALLOCATION");

  Enquiry2 = slice_dma_loaf(&local_dma, sizeof(DAC960_V1_Enquiry2_T), &Enquiry2DMA);
  Config2 = slice_dma_loaf(&local_dma, sizeof(DAC960_V1_Config2_T), &Config2DMA);

  if (!DAC960_V1_ExecuteType3(Controller, DAC960_V1_Enquiry,
			      Controller->V1.NewEnquiryDMA)) {
    free_dma_loaf(Controller->PCIDevice, &local_dma);
    return DAC960_Failure(Controller, "ENQUIRY");
  }
  memcpy(&Controller->V1.Enquiry, Controller->V1.NewEnquiry,
						sizeof(DAC960_V1_Enquiry_T));

  if (!DAC960_V1_ExecuteType3(Controller, DAC960_V1_Enquiry2, Enquiry2DMA)) {
    free_dma_loaf(Controller->PCIDevice, &local_dma);
    return DAC960_Failure(Controller, "ENQUIRY2");
  }

  if (!DAC960_V1_ExecuteType3(Controller, DAC960_V1_ReadConfig2, Config2DMA)) {
    free_dma_loaf(Controller->PCIDevice, &local_dma);
    return DAC960_Failure(Controller, "READ CONFIG2");
  }

  if (!DAC960_V1_ExecuteType3(Controller, DAC960_V1_GetLogicalDriveInformation,
			      Controller->V1.NewLogicalDriveInformationDMA)) {
    free_dma_loaf(Controller->PCIDevice, &local_dma);
    return DAC960_Failure(Controller, "GET LOGICAL DRIVE INFORMATION");
  }
  memcpy(&Controller->V1.LogicalDriveInformation,
		Controller->V1.NewLogicalDriveInformation,
		sizeof(DAC960_V1_LogicalDriveInformationArray_T));

  for (Channel = 0; Channel < Enquiry2->ActualChannels; Channel++)
    for (TargetID = 0; TargetID < Enquiry2->MaxTargets; TargetID++) {
      if (!DAC960_V1_ExecuteType3D(Controller, DAC960_V1_GetDeviceState,
				   Channel, TargetID,
				   Controller->V1.NewDeviceStateDMA)) {
    		free_dma_loaf(Controller->PCIDevice, &local_dma);
		return DAC960_Failure(Controller, "GET DEVICE STATE");
	}
	memcpy(&Controller->V1.DeviceState[Channel][TargetID],
		Controller->V1.NewDeviceState, sizeof(DAC960_V1_DeviceState_T));
     }
  /*
    Initialize the Controller Model Name and Full Model Name fields.
  */
  switch (Enquiry2->HardwareID.SubModel)
    {
    case DAC960_V1_P_PD_PU:
      if (Enquiry2->SCSICapability.BusSpeed == DAC960_V1_Ultra)
	strcpy(Controller->ModelName, "DAC960PU");
      else strcpy(Controller->ModelName, "DAC960PD");
      break;
    case DAC960_V1_PL:
      strcpy(Controller->ModelName, "DAC960PL");
      break;
    case DAC960_V1_PG:
      strcpy(Controller->ModelName, "DAC960PG");
      break;
    case DAC960_V1_PJ:
      strcpy(Controller->ModelName, "DAC960PJ");
      break;
    case DAC960_V1_PR:
      strcpy(Controller->ModelName, "DAC960PR");
      break;
    case DAC960_V1_PT:
      strcpy(Controller->ModelName, "DAC960PT");
      break;
    case DAC960_V1_PTL0:
      strcpy(Controller->ModelName, "DAC960PTL0");
      break;
    case DAC960_V1_PRL:
      strcpy(Controller->ModelName, "DAC960PRL");
      break;
    case DAC960_V1_PTL1:
      strcpy(Controller->ModelName, "DAC960PTL1");
      break;
    case DAC960_V1_1164P:
      strcpy(Controller->ModelName, "DAC1164P");
      break;
    default:
      free_dma_loaf(Controller->PCIDevice, &local_dma);
      return DAC960_Failure(Controller, "MODEL VERIFICATION");
    }
  strcpy(Controller->FullModelName, "Mylex ");
  strcat(Controller->FullModelName, Controller->ModelName);
  /*
    Initialize the Controller Firmware Version field and verify that it
    is a supported firmware version.  The supported firmware versions are:

    DAC1164P		    5.06 and above
    DAC960PTL/PRL/PJ/PG	    4.06 and above
    DAC960PU/PD/PL	    3.51 and above
    DAC960PU/PD/PL/P	    2.73 and above
  */
#if defined(CONFIG_ALPHA)
  /*
    DEC Alpha machines were often equipped with DAC960 cards that were
    OEMed from Mylex, and had their own custom firmware. Version 2.70,
    the last custom FW revision to be released by DEC for these older
    controllers, appears to work quite well with this driver.

    Cards tested successfully were several versions each of the PD and
    PU, called by DEC the KZPSC and KZPAC, respectively, and having
    the Manufacturer Numbers (from Mylex), usually on a sticker on the
    back of the board, of:

    KZPSC:  D040347 (1-channel) or D040348 (2-channel) or D040349 (3-channel)
    KZPAC:  D040395 (1-channel) or D040396 (2-channel) or D040397 (3-channel)
  */
# define FIRMWARE_27X	"2.70"
#else
# define FIRMWARE_27X	"2.73"
#endif

  if (Enquiry2->FirmwareID.MajorVersion == 0)
    {
      Enquiry2->FirmwareID.MajorVersion =
	Controller->V1.Enquiry.MajorFirmwareVersion;
      Enquiry2->FirmwareID.MinorVersion =
	Controller->V1.Enquiry.MinorFirmwareVersion;
      Enquiry2->FirmwareID.FirmwareType = '0';
      Enquiry2->FirmwareID.TurnID = 0;
    }
  sprintf(Controller->FirmwareVersion, "%d.%02d-%c-%02d",
	  Enquiry2->FirmwareID.MajorVersion, Enquiry2->FirmwareID.MinorVersion,
	  Enquiry2->FirmwareID.FirmwareType, Enquiry2->FirmwareID.TurnID);
  if (!((Controller->FirmwareVersion[0] == '5' &&
	 strcmp(Controller->FirmwareVersion, "5.06") >= 0) ||
	(Controller->FirmwareVersion[0] == '4' &&
	 strcmp(Controller->FirmwareVersion, "4.06") >= 0) ||
	(Controller->FirmwareVersion[0] == '3' &&
	 strcmp(Controller->FirmwareVersion, "3.51") >= 0) ||
	(Controller->FirmwareVersion[0] == '2' &&
	 strcmp(Controller->FirmwareVersion, FIRMWARE_27X) >= 0)))
    {
      DAC960_Failure(Controller, "FIRMWARE VERSION VERIFICATION");
      DAC960_Error("Firmware Version = '%s'\n", Controller,
		   Controller->FirmwareVersion);
      free_dma_loaf(Controller->PCIDevice, &local_dma);
      return false;
    }
  /*
    Initialize the Controller Channels, Targets, Memory Size, and SAF-TE
    Enclosure Management Enabled fields.
  */
  Controller->Channels = Enquiry2->ActualChannels;
  Controller->Targets = Enquiry2->MaxTargets;
  Controller->MemorySize = Enquiry2->MemorySize >> 20;
  Controller->V1.SAFTE_EnclosureManagementEnabled =
    (Enquiry2->FaultManagementType == DAC960_V1_SAFTE);
  /*
    Initialize the Controller Queue Depth, Driver Queue Depth, Logical Drive
    Count, Maximum Blocks per Command, Controller Scatter/Gather Limit, and
    Driver Scatter/Gather Limit.  The Driver Queue Depth must be at most one
    less than the Controller Queue Depth to allow for an automatic drive
    rebuild operation.
  */
  Controller->ControllerQueueDepth = Controller->V1.Enquiry.MaxCommands;
  Controller->DriverQueueDepth = Controller->ControllerQueueDepth - 1;
  if (Controller->DriverQueueDepth > DAC960_MaxDriverQueueDepth)
    Controller->DriverQueueDepth = DAC960_MaxDriverQueueDepth;
  Controller->LogicalDriveCount =
    Controller->V1.Enquiry.NumberOfLogicalDrives;
  Controller->MaxBlocksPerCommand = Enquiry2->MaxBlocksPerCommand;
  Controller->ControllerScatterGatherLimit = Enquiry2->MaxScatterGatherEntries;
  Controller->DriverScatterGatherLimit =
    Controller->ControllerScatterGatherLimit;
  if (Controller->DriverScatterGatherLimit > DAC960_V1_ScatterGatherLimit)
    Controller->DriverScatterGatherLimit = DAC960_V1_ScatterGatherLimit;
  /*
    Initialize the Stripe Size, Segment Size, and Geometry Translation.
  */
  Controller->V1.StripeSize = Config2->BlocksPerStripe * Config2->BlockFactor
			      >> (10 - DAC960_BlockSizeBits);
  Controller->V1.SegmentSize = Config2->BlocksPerCacheLine * Config2->BlockFactor
			       >> (10 - DAC960_BlockSizeBits);
  switch (Config2->DriveGeometry)
    {
    case DAC960_V1_Geometry_128_32:
      Controller->V1.GeometryTranslationHeads = 128;
      Controller->V1.GeometryTranslationSectors = 32;
      break;
    case DAC960_V1_Geometry_255_63:
      Controller->V1.GeometryTranslationHeads = 255;
      Controller->V1.GeometryTranslationSectors = 63;
      break;
    default:
      free_dma_loaf(Controller->PCIDevice, &local_dma);
      return DAC960_Failure(Controller, "CONFIG2 DRIVE GEOMETRY");
    }
  /*
    Initialize the Background Initialization Status.
  */
  if ((Controller->FirmwareVersion[0] == '4' &&
      strcmp(Controller->FirmwareVersion, "4.08") >= 0) ||
      (Controller->FirmwareVersion[0] == '5' &&
       strcmp(Controller->FirmwareVersion, "5.08") >= 0))
    {
      Controller->V1.BackgroundInitializationStatusSupported = true;
      DAC960_V1_ExecuteType3B(Controller,
			      DAC960_V1_BackgroundInitializationControl, 0x20,
			      Controller->
			       V1.BackgroundInitializationStatusDMA);
      memcpy(&Controller->V1.LastBackgroundInitializationStatus,
		Controller->V1.BackgroundInitializationStatus,
		sizeof(DAC960_V1_BackgroundInitializationStatus_T));
    }
  /*
    Initialize the Logical Drive Initially Accessible flag.
  */
  for (LogicalDriveNumber = 0;
       LogicalDriveNumber < Controller->LogicalDriveCount;
       LogicalDriveNumber++)
    if (Controller->V1.LogicalDriveInformation
		       [LogicalDriveNumber].LogicalDriveState !=
	DAC960_V1_LogicalDrive_Offline)
      Controller->LogicalDriveInitiallyAccessible[LogicalDriveNumber] = true;
  Controller->V1.LastRebuildStatus = DAC960_V1_NoRebuildOrCheckInProgress;
  free_dma_loaf(Controller->PCIDevice, &local_dma);
  return true;
}


/*
  DAC960_V2_ReadControllerConfiguration reads the Configuration Information
  from DAC960 V2 Firmware Controllers and initializes the Controller structure.
*/

static bool DAC960_V2_ReadControllerConfiguration(DAC960_Controller_T
						     *Controller)
{
  DAC960_V2_ControllerInfo_T *ControllerInfo =
    		&Controller->V2.ControllerInformation;
  unsigned short LogicalDeviceNumber = 0;
  int ModelNameLength;

  /* Get data into dma-able area, then copy into permanant location */
  if (!DAC960_V2_NewControllerInfo(Controller))
    return DAC960_Failure(Controller, "GET CONTROLLER INFO");
  memcpy(ControllerInfo, Controller->V2.NewControllerInformation,
			sizeof(DAC960_V2_ControllerInfo_T));
	 
  
  if (!DAC960_V2_GeneralInfo(Controller))
    return DAC960_Failure(Controller, "GET HEALTH STATUS");

  /*
    Initialize the Controller Model Name and Full Model Name fields.
  */
  ModelNameLength = sizeof(ControllerInfo->ControllerName);
  if (ModelNameLength > sizeof(Controller->ModelName)-1)
    ModelNameLength = sizeof(Controller->ModelName)-1;
  memcpy(Controller->ModelName, ControllerInfo->ControllerName,
	 ModelNameLength);
  ModelNameLength--;
  while (Controller->ModelName[ModelNameLength] == ' ' ||
	 Controller->ModelName[ModelNameLength] == '\0')
    ModelNameLength--;
  Controller->ModelName[++ModelNameLength] = '\0';
  strcpy(Controller->FullModelName, "Mylex ");
  strcat(Controller->FullModelName, Controller->ModelName);
  /*
    Initialize the Controller Firmware Version field.
  */
  sprintf(Controller->FirmwareVersion, "%d.%02d-%02d",
	  ControllerInfo->FirmwareMajorVersion,
	  ControllerInfo->FirmwareMinorVersion,
	  ControllerInfo->FirmwareTurnNumber);
  if (ControllerInfo->FirmwareMajorVersion == 6 &&
      ControllerInfo->FirmwareMinorVersion == 0 &&
      ControllerInfo->FirmwareTurnNumber < 1)
    {
      DAC960_Info("FIRMWARE VERSION %s DOES NOT PROVIDE THE CONTROLLER\n",
		  Controller, Controller->FirmwareVersion);
      DAC960_Info("STATUS MONITORING FUNCTIONALITY NEEDED BY THIS DRIVER.\n",
		  Controller);
      DAC960_Info("PLEASE UPGRADE TO VERSION 6.00-01 OR ABOVE.\n",
		  Controller);
    }
  /*
    Initialize the Controller Channels, Targets, and Memory Size.
  */
  Controller->Channels = ControllerInfo->NumberOfPhysicalChannelsPresent;
  Controller->Targets =
    ControllerInfo->MaximumTargetsPerChannel
		    [ControllerInfo->NumberOfPhysicalChannelsPresent-1];
  Controller->MemorySize = ControllerInfo->MemorySizeMB;
  /*
    Initialize the Controller Queue Depth, Driver Queue Depth, Logical Drive
    Count, Maximum Blocks per Command, Controller Scatter/Gather Limit, and
    Driver Scatter/Gather Limit.  The Driver Queue Depth must be at most one
    less than the Controller Queue Depth to allow for an automatic drive
    rebuild operation.
  */
  Controller->ControllerQueueDepth = ControllerInfo->MaximumParallelCommands;
  Controller->DriverQueueDepth = Controller->ControllerQueueDepth - 1;
  if (Controller->DriverQueueDepth > DAC960_MaxDriverQueueDepth)
    Controller->DriverQueueDepth = DAC960_MaxDriverQueueDepth;
  Controller->LogicalDriveCount = ControllerInfo->LogicalDevicesPresent;
  Controller->MaxBlocksPerCommand =
    ControllerInfo->MaximumDataTransferSizeInBlocks;
  Controller->ControllerScatterGatherLimit =
    ControllerInfo->MaximumScatterGatherEntries;
  Controller->DriverScatterGatherLimit =
    Controller->ControllerScatterGatherLimit;
  if (Controller->DriverScatterGatherLimit > DAC960_V2_ScatterGatherLimit)
    Controller->DriverScatterGatherLimit = DAC960_V2_ScatterGatherLimit;
  /*
    Initialize the Logical Device Information.
  */
  while (true)
    {
      DAC960_V2_LogicalDeviceInfo_T *NewLogicalDeviceInfo =
	Controller->V2.NewLogicalDeviceInformation;
      DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo;
      DAC960_V2_PhysicalDevice_T PhysicalDevice;

      if (!DAC960_V2_NewLogicalDeviceInfo(Controller, LogicalDeviceNumber))
	break;
      LogicalDeviceNumber = NewLogicalDeviceInfo->LogicalDeviceNumber;
      if (LogicalDeviceNumber >= DAC960_MaxLogicalDrives) {
	DAC960_Error("DAC960: Logical Drive Number %d not supported\n",
		       Controller, LogicalDeviceNumber);
		break;
      }
      if (NewLogicalDeviceInfo->DeviceBlockSizeInBytes != DAC960_BlockSize) {
	DAC960_Error("DAC960: Logical Drive Block Size %d not supported\n",
	      Controller, NewLogicalDeviceInfo->DeviceBlockSizeInBytes);
        LogicalDeviceNumber++;
        continue;
      }
      PhysicalDevice.Controller = 0;
      PhysicalDevice.Channel = NewLogicalDeviceInfo->Channel;
      PhysicalDevice.TargetID = NewLogicalDeviceInfo->TargetID;
      PhysicalDevice.LogicalUnit = NewLogicalDeviceInfo->LogicalUnit;
      Controller->V2.LogicalDriveToVirtualDevice[LogicalDeviceNumber] =
	PhysicalDevice;
      if (NewLogicalDeviceInfo->LogicalDeviceState !=
	  DAC960_V2_LogicalDevice_Offline)
	Controller->LogicalDriveInitiallyAccessible[LogicalDeviceNumber] = true;
      LogicalDeviceInfo = kmalloc(sizeof(DAC960_V2_LogicalDeviceInfo_T),
				   GFP_ATOMIC);
      if (LogicalDeviceInfo == NULL)
	return DAC960_Failure(Controller, "LOGICAL DEVICE ALLOCATION");
      Controller->V2.LogicalDeviceInformation[LogicalDeviceNumber] =
	LogicalDeviceInfo;
      memcpy(LogicalDeviceInfo, NewLogicalDeviceInfo,
	     sizeof(DAC960_V2_LogicalDeviceInfo_T));
      LogicalDeviceNumber++;
    }
  return true;
}


/*
  DAC960_ReportControllerConfiguration reports the Configuration Information
  for Controller.
*/

static bool DAC960_ReportControllerConfiguration(DAC960_Controller_T
						    *Controller)
{
  DAC960_Info("Configuring Mylex %s PCI RAID Controller\n",
	      Controller, Controller->ModelName);
  DAC960_Info("  Firmware Version: %s, Channels: %d, Memory Size: %dMB\n",
	      Controller, Controller->FirmwareVersion,
	      Controller->Channels, Controller->MemorySize);
  DAC960_Info("  PCI Bus: %d, Device: %d, Function: %d, I/O Address: ",
	      Controller, Controller->Bus,
	      Controller->Device, Controller->Function);
  if (Controller->IO_Address == 0)
    DAC960_Info("Unassigned\n", Controller);
  else DAC960_Info("0x%X\n", Controller, Controller->IO_Address);
  DAC960_Info("  PCI Address: 0x%X mapped at 0x%lX, IRQ Channel: %d\n",
	      Controller, Controller->PCI_Address,
	      (unsigned long) Controller->BaseAddress,
	      Controller->IRQ_Channel);
  DAC960_Info("  Controller Queue Depth: %d, "
	      "Maximum Blocks per Command: %d\n",
	      Controller, Controller->ControllerQueueDepth,
	      Controller->MaxBlocksPerCommand);
  DAC960_Info("  Driver Queue Depth: %d, "
	      "Scatter/Gather Limit: %d of %d Segments\n",
	      Controller, Controller->DriverQueueDepth,
	      Controller->DriverScatterGatherLimit,
	      Controller->ControllerScatterGatherLimit);
  if (Controller->FirmwareType == DAC960_V1_Controller)
    {
      DAC960_Info("  Stripe Size: %dKB, Segment Size: %dKB, "
		  "BIOS Geometry: %d/%d\n", Controller,
		  Controller->V1.StripeSize,
		  Controller->V1.SegmentSize,
		  Controller->V1.GeometryTranslationHeads,
		  Controller->V1.GeometryTranslationSectors);
      if (Controller->V1.SAFTE_EnclosureManagementEnabled)
	DAC960_Info("  SAF-TE Enclosure Management Enabled\n", Controller);
    }
  return true;
}


/*
  DAC960_V1_ReadDeviceConfiguration reads the Device Configuration Information
  for DAC960 V1 Firmware Controllers by requesting the SCSI Inquiry and SCSI
  Inquiry Unit Serial Number information for each device connected to
  Controller.
*/

static bool DAC960_V1_ReadDeviceConfiguration(DAC960_Controller_T
						 *Controller)
{
  struct dma_loaf local_dma;

  dma_addr_t DCDBs_dma[DAC960_V1_MaxChannels];
  DAC960_V1_DCDB_T *DCDBs_cpu[DAC960_V1_MaxChannels];

  dma_addr_t SCSI_Inquiry_dma[DAC960_V1_MaxChannels];
  DAC960_SCSI_Inquiry_T *SCSI_Inquiry_cpu[DAC960_V1_MaxChannels];

  dma_addr_t SCSI_NewInquiryUnitSerialNumberDMA[DAC960_V1_MaxChannels];
  DAC960_SCSI_Inquiry_UnitSerialNumber_T *SCSI_NewInquiryUnitSerialNumberCPU[DAC960_V1_MaxChannels];

  struct completion Completions[DAC960_V1_MaxChannels];
  unsigned long flags;
  int Channel, TargetID;

  if (!init_dma_loaf(Controller->PCIDevice, &local_dma, 
		DAC960_V1_MaxChannels*(sizeof(DAC960_V1_DCDB_T) +
			sizeof(DAC960_SCSI_Inquiry_T) +
			sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T))))
     return DAC960_Failure(Controller,
                        "DMA ALLOCATION FAILED IN ReadDeviceConfiguration"); 
   
  for (Channel = 0; Channel < Controller->Channels; Channel++) {
	DCDBs_cpu[Channel] = slice_dma_loaf(&local_dma,
			sizeof(DAC960_V1_DCDB_T), DCDBs_dma + Channel);
	SCSI_Inquiry_cpu[Channel] = slice_dma_loaf(&local_dma,
			sizeof(DAC960_SCSI_Inquiry_T),
			SCSI_Inquiry_dma + Channel);
	SCSI_NewInquiryUnitSerialNumberCPU[Channel] = slice_dma_loaf(&local_dma,
			sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T),
			SCSI_NewInquiryUnitSerialNumberDMA + Channel);
  }
		
  for (TargetID = 0; TargetID < Controller->Targets; TargetID++)
    {
      /*
       * For each channel, submit a probe for a device on that channel.
       * The timeout interval for a device that is present is 10 seconds.
       * With this approach, the timeout periods can elapse in parallel
       * on each channel.
       */
      for (Channel = 0; Channel < Controller->Channels; Channel++)
	{
	  dma_addr_t NewInquiryStandardDataDMA = SCSI_Inquiry_dma[Channel];
  	  DAC960_V1_DCDB_T *DCDB = DCDBs_cpu[Channel];
  	  dma_addr_t DCDB_dma = DCDBs_dma[Channel];
	  DAC960_Command_T *Command = Controller->Commands[Channel];
          struct completion *Completion = &Completions[Channel];

	  init_completion(Completion);
	  DAC960_V1_ClearCommand(Command);
	  Command->CommandType = DAC960_ImmediateCommand;
	  Command->Completion = Completion;
	  Command->V1.CommandMailbox.Type3.CommandOpcode = DAC960_V1_DCDB;
	  Command->V1.CommandMailbox.Type3.BusAddress = DCDB_dma;
	  DCDB->Channel = Channel;
	  DCDB->TargetID = TargetID;
	  DCDB->Direction = DAC960_V1_DCDB_DataTransferDeviceToSystem;
	  DCDB->EarlyStatus = false;
	  DCDB->Timeout = DAC960_V1_DCDB_Timeout_10_seconds;
	  DCDB->NoAutomaticRequestSense = false;
	  DCDB->DisconnectPermitted = true;
	  DCDB->TransferLength = sizeof(DAC960_SCSI_Inquiry_T);
	  DCDB->BusAddress = NewInquiryStandardDataDMA;
	  DCDB->CDBLength = 6;
	  DCDB->TransferLengthHigh4 = 0;
	  DCDB->SenseLength = sizeof(DCDB->SenseData);
	  DCDB->CDB[0] = 0x12; /* INQUIRY */
	  DCDB->CDB[1] = 0; /* EVPD = 0 */
	  DCDB->CDB[2] = 0; /* Page Code */
	  DCDB->CDB[3] = 0; /* Reserved */
	  DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_T);
	  DCDB->CDB[5] = 0; /* Control */

	  spin_lock_irqsave(&Controller->queue_lock, flags);
	  DAC960_QueueCommand(Command);
	  spin_unlock_irqrestore(&Controller->queue_lock, flags);
	}
      /*
       * Wait for the problems submitted in the previous loop
       * to complete.  On the probes that are successful, 
       * get the serial number of the device that was found.
       */
      for (Channel = 0; Channel < Controller->Channels; Channel++)
	{
	  DAC960_SCSI_Inquiry_T *InquiryStandardData =
	    &Controller->V1.InquiryStandardData[Channel][TargetID];
	  DAC960_SCSI_Inquiry_T *NewInquiryStandardData = SCSI_Inquiry_cpu[Channel];
	  dma_addr_t NewInquiryUnitSerialNumberDMA =
			SCSI_NewInquiryUnitSerialNumberDMA[Channel];
	  DAC960_SCSI_Inquiry_UnitSerialNumber_T *NewInquiryUnitSerialNumber =
	    		SCSI_NewInquiryUnitSerialNumberCPU[Channel];
	  DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
	    &Controller->V1.InquiryUnitSerialNumber[Channel][TargetID];
	  DAC960_Command_T *Command = Controller->Commands[Channel];
  	  DAC960_V1_DCDB_T *DCDB = DCDBs_cpu[Channel];
          struct completion *Completion = &Completions[Channel];

	  wait_for_completion(Completion);

	  if (Command->V1.CommandStatus != DAC960_V1_NormalCompletion) {
	    memset(InquiryStandardData, 0, sizeof(DAC960_SCSI_Inquiry_T));
	    InquiryStandardData->PeripheralDeviceType = 0x1F;
	    continue;
	  } else
	    memcpy(InquiryStandardData, NewInquiryStandardData, sizeof(DAC960_SCSI_Inquiry_T));
	
	  /* Preserve Channel and TargetID values from the previous loop */
	  Command->Completion = Completion;
	  DCDB->TransferLength = sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
	  DCDB->BusAddress = NewInquiryUnitSerialNumberDMA;
	  DCDB->SenseLength = sizeof(DCDB->SenseData);
	  DCDB->CDB[0] = 0x12; /* INQUIRY */
	  DCDB->CDB[1] = 1; /* EVPD = 1 */
	  DCDB->CDB[2] = 0x80; /* Page Code */
	  DCDB->CDB[3] = 0; /* Reserved */
	  DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
	  DCDB->CDB[5] = 0; /* Control */

	  spin_lock_irqsave(&Controller->queue_lock, flags);
	  DAC960_QueueCommand(Command);
	  spin_unlock_irqrestore(&Controller->queue_lock, flags);
	  wait_for_completion(Completion);

	  if (Command->V1.CommandStatus != DAC960_V1_NormalCompletion) {
	  	memset(InquiryUnitSerialNumber, 0,
			sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
	  	InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
	  } else
	  	memcpy(InquiryUnitSerialNumber, NewInquiryUnitSerialNumber,
			sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
	}
    }
    free_dma_loaf(Controller->PCIDevice, &local_dma);
  return true;
}


/*
  DAC960_V2_ReadDeviceConfiguration reads the Device Configuration Information
  for DAC960 V2 Firmware Controllers by requesting the Physical Device
  Information and SCSI Inquiry Unit Serial Number information for each
  device connected to Controller.
*/

static bool DAC960_V2_ReadDeviceConfiguration(DAC960_Controller_T
						 *Controller)
{
  unsigned char Channel = 0, TargetID = 0, LogicalUnit = 0;
  unsigned short PhysicalDeviceIndex = 0;

  while (true)
    {
      DAC960_V2_PhysicalDeviceInfo_T *NewPhysicalDeviceInfo =
		Controller->V2.NewPhysicalDeviceInformation;
      DAC960_V2_PhysicalDeviceInfo_T *PhysicalDeviceInfo;
      DAC960_SCSI_Inquiry_UnitSerialNumber_T *NewInquiryUnitSerialNumber =
		Controller->V2.NewInquiryUnitSerialNumber;
      DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber;

      if (!DAC960_V2_NewPhysicalDeviceInfo(Controller, Channel, TargetID, LogicalUnit))
	  break;

      PhysicalDeviceInfo = kmalloc(sizeof(DAC960_V2_PhysicalDeviceInfo_T),
				    GFP_ATOMIC);
      if (PhysicalDeviceInfo == NULL)
		return DAC960_Failure(Controller, "PHYSICAL DEVICE ALLOCATION");
      Controller->V2.PhysicalDeviceInformation[PhysicalDeviceIndex] =
		PhysicalDeviceInfo;
      memcpy(PhysicalDeviceInfo, NewPhysicalDeviceInfo,
		sizeof(DAC960_V2_PhysicalDeviceInfo_T));

      InquiryUnitSerialNumber = kmalloc(
	      sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T), GFP_ATOMIC);
      if (InquiryUnitSerialNumber == NULL) {
	kfree(PhysicalDeviceInfo);
	return DAC960_Failure(Controller, "SERIAL NUMBER ALLOCATION");
      }
      Controller->V2.InquiryUnitSerialNumber[PhysicalDeviceIndex] =
		InquiryUnitSerialNumber;

      Channel = NewPhysicalDeviceInfo->Channel;
      TargetID = NewPhysicalDeviceInfo->TargetID;
      LogicalUnit = NewPhysicalDeviceInfo->LogicalUnit;

      /*
	 Some devices do NOT have Unit Serial Numbers.
	 This command fails for them.  But, we still want to
	 remember those devices are there.  Construct a
	 UnitSerialNumber structure for the failure case.
      */
      if (!DAC960_V2_NewInquiryUnitSerialNumber(Controller, Channel, TargetID, LogicalUnit)) {
      	memset(InquiryUnitSerialNumber, 0,
             sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
     	InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
      } else
      	memcpy(InquiryUnitSerialNumber, NewInquiryUnitSerialNumber,
		sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));

      PhysicalDeviceIndex++;
      LogicalUnit++;
    }
  return true;
}


/*
  DAC960_SanitizeInquiryData sanitizes the Vendor, Model, Revision, and
  Product Serial Number fields of the Inquiry Standard Data and Inquiry
  Unit Serial Number structures.
*/

static void DAC960_SanitizeInquiryData(DAC960_SCSI_Inquiry_T
					 *InquiryStandardData,
				       DAC960_SCSI_Inquiry_UnitSerialNumber_T
					 *InquiryUnitSerialNumber,
				       unsigned char *Vendor,
				       unsigned char *Model,
				       unsigned char *Revision,
				       unsigned char *SerialNumber)
{
  int SerialNumberLength, i;
  if (InquiryStandardData->PeripheralDeviceType == 0x1F) return;
  for (i = 0; i < sizeof(InquiryStandardData->VendorIdentification); i++)
    {
      unsigned char VendorCharacter =
	InquiryStandardData->VendorIdentification[i];
      Vendor[i] = (VendorCharacter >= ' ' && VendorCharacter <= '~'
		   ? VendorCharacter : ' ');
    }
  Vendor[sizeof(InquiryStandardData->VendorIdentification)] = '\0';
  for (i = 0; i < sizeof(InquiryStandardData->ProductIdentification); i++)
    {
      unsigned char ModelCharacter =
	InquiryStandardData->ProductIdentification[i];
      Model[i] = (ModelCharacter >= ' ' && ModelCharacter <= '~'
		  ? ModelCharacter : ' ');
    }
  Model[sizeof(InquiryStandardData->ProductIdentification)] = '\0';
  for (i = 0; i < sizeof(InquiryStandardData->ProductRevisionLevel); i++)
    {
      unsigned char RevisionCharacter =
	InquiryStandardData->ProductRevisionLevel[i];
      Revision[i] = (RevisionCharacter >= ' ' && RevisionCharacter <= '~'
		     ? RevisionCharacter : ' ');
    }
  Revision[sizeof(InquiryStandardData->ProductRevisionLevel)] = '\0';
  if (InquiryUnitSerialNumber->PeripheralDeviceType == 0x1F) return;
  SerialNumberLength = InquiryUnitSerialNumber->PageLength;
  if (SerialNumberLength >
      sizeof(InquiryUnitSerialNumber->ProductSerialNumber))
    SerialNumberLength = sizeof(InquiryUnitSerialNumber->ProductSerialNumber);
  for (i = 0; i < SerialNumberLength; i++)
    {
      unsigned char SerialNumberCharacter =
	InquiryUnitSerialNumber->ProductSerialNumber[i];
      SerialNumber[i] =
	(SerialNumberCharacter >= ' ' && SerialNumberCharacter <= '~'
	 ? SerialNumberCharacter : ' ');
    }
  SerialNumber[SerialNumberLength] = '\0';
}


/*
  DAC960_V1_ReportDeviceConfiguration reports the Device Configuration
  Information for DAC960 V1 Firmware Controllers.
*/

static bool DAC960_V1_ReportDeviceConfiguration(DAC960_Controller_T
						   *Controller)
{
  int LogicalDriveNumber, Channel, TargetID;
  DAC960_Info("  Physical Devices:\n", Controller);
  for (Channel = 0; Channel < Controller->Channels; Channel++)
    for (TargetID = 0; TargetID < Controller->Targets; TargetID++)
      {
	DAC960_SCSI_Inquiry_T *InquiryStandardData =
	  &Controller->V1.InquiryStandardData[Channel][TargetID];
	DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
	  &Controller->V1.InquiryUnitSerialNumber[Channel][TargetID];
	DAC960_V1_DeviceState_T *DeviceState =
	  &Controller->V1.DeviceState[Channel][TargetID];
	DAC960_V1_ErrorTableEntry_T *ErrorEntry =
	  &Controller->V1.ErrorTable.ErrorTableEntries[Channel][TargetID];
	char Vendor[1+sizeof(InquiryStandardData->VendorIdentification)];
	char Model[1+sizeof(InquiryStandardData->ProductIdentification)];
	char Revision[1+sizeof(InquiryStandardData->ProductRevisionLevel)];
	char SerialNumber[1+sizeof(InquiryUnitSerialNumber
				   ->ProductSerialNumber)];
	if (InquiryStandardData->PeripheralDeviceType == 0x1F) continue;
	DAC960_SanitizeInquiryData(InquiryStandardData, InquiryUnitSerialNumber,
				   Vendor, Model, Revision, SerialNumber);
	DAC960_Info("    %d:%d%s Vendor: %s  Model: %s  Revision: %s\n",
		    Controller, Channel, TargetID, (TargetID < 10 ? " " : ""),
		    Vendor, Model, Revision);
	if (InquiryUnitSerialNumber->PeripheralDeviceType != 0x1F)
	  DAC960_Info("         Serial Number: %s\n", Controller, SerialNumber);
	if (DeviceState->Present &&
	    DeviceState->DeviceType == DAC960_V1_DiskType)
	  {
	    if (Controller->V1.DeviceResetCount[Channel][TargetID] > 0)
	      DAC960_Info("         Disk Status: %s, %u blocks, %d resets\n",
			  Controller,
			  (DeviceState->DeviceState == DAC960_V1_Device_Dead
			   ? "Dead"
			   : DeviceState->DeviceState
			     == DAC960_V1_Device_WriteOnly
			     ? "Write-Only"
			     : DeviceState->DeviceState
			       == DAC960_V1_Device_Online
			       ? "Online" : "Standby"),
			  DeviceState->DiskSize,
			  Controller->V1.DeviceResetCount[Channel][TargetID]);
	    else
	      DAC960_Info("         Disk Status: %s, %u blocks\n", Controller,
			  (DeviceState->DeviceState == DAC960_V1_Device_Dead
			   ? "Dead"
			   : DeviceState->DeviceState
			     == DAC960_V1_Device_WriteOnly
			     ? "Write-Only"
			     : DeviceState->DeviceState
			       == DAC960_V1_Device_Online
			       ? "Online" : "Standby"),
			  DeviceState->DiskSize);
	  }
	if (ErrorEntry->ParityErrorCount > 0 ||
	    ErrorEntry->SoftErrorCount > 0 ||
	    ErrorEntry->HardErrorCount > 0 ||
	    ErrorEntry->MiscErrorCount > 0)
	  DAC960_Info("         Errors - Parity: %d, Soft: %d, "
		      "Hard: %d, Misc: %d\n", Controller,
		      ErrorEntry->ParityErrorCount,
		      ErrorEntry->SoftErrorCount,
		      ErrorEntry->HardErrorCount,
		      ErrorEntry->MiscErrorCount);
      }
  DAC960_Info("  Logical Drives:\n", Controller);
  for (LogicalDriveNumber = 0;
       LogicalDriveNumber < Controller->LogicalDriveCount;
       LogicalDriveNumber++)
    {
      DAC960_V1_LogicalDriveInformation_T *LogicalDriveInformation =
	&Controller->V1.LogicalDriveInformation[LogicalDriveNumber];
      DAC960_Info("    /dev/rd/c%dd%d: RAID-%d, %s, %u blocks, %s\n",
		  Controller, Controller->ControllerNumber, LogicalDriveNumber,
		  LogicalDriveInformation->RAIDLevel,
		  (LogicalDriveInformation->LogicalDriveState
		   == DAC960_V1_LogicalDrive_Online
		   ? "Online"
		   : LogicalDriveInformation->LogicalDriveState
		     == DAC960_V1_LogicalDrive_Critical
		     ? "Critical" : "Offline"),
		  LogicalDriveInformation->LogicalDriveSize,
		  (LogicalDriveInformation->WriteBack
		   ? "Write Back" : "Write Thru"));
    }
  return true;
}


/*
  DAC960_V2_ReportDeviceConfiguration reports the Device Configuration
  Information for DAC960 V2 Firmware Controllers.
*/

static bool DAC960_V2_ReportDeviceConfiguration(DAC960_Controller_T
						   *Controller)
{
  int PhysicalDeviceIndex, LogicalDriveNumber;
  DAC960_Info("  Physical Devices:\n", Controller);
  for (PhysicalDeviceIndex = 0;
       PhysicalDeviceIndex < DAC960_V2_MaxPhysicalDevices;
       PhysicalDeviceIndex++)
    {
      DAC960_V2_PhysicalDeviceInfo_T *PhysicalDeviceInfo =
	Controller->V2.PhysicalDeviceInformation[PhysicalDeviceIndex];
      DAC960_SCSI_Inquiry_T *InquiryStandardData =
	(DAC960_SCSI_Inquiry_T *) &PhysicalDeviceInfo->SCSI_InquiryData;
      DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
	Controller->V2.InquiryUnitSerialNumber[PhysicalDeviceIndex];
      char Vendor[1+sizeof(InquiryStandardData->VendorIdentification)];
      char Model[1+sizeof(InquiryStandardData->ProductIdentification)];
      char Revision[1+sizeof(InquiryStandardData->ProductRevisionLevel)];
      char SerialNumber[1+sizeof(InquiryUnitSerialNumber->ProductSerialNumber)];
      if (PhysicalDeviceInfo == NULL) break;
      DAC960_SanitizeInquiryData(InquiryStandardData, InquiryUnitSerialNumber,
				 Vendor, Model, Revision, SerialNumber);
      DAC960_Info("    %d:%d%s Vendor: %s  Model: %s  Revision: %s\n",
		  Controller,
		  PhysicalDeviceInfo->Channel,
		  PhysicalDeviceInfo->TargetID,
		  (PhysicalDeviceInfo->TargetID < 10 ? " " : ""),
		  Vendor, Model, Revision);
      if (PhysicalDeviceInfo->NegotiatedSynchronousMegaTransfers == 0)
	DAC960_Info("         %sAsynchronous\n", Controller,
		    (PhysicalDeviceInfo->NegotiatedDataWidthBits == 16
		     ? "Wide " :""));
      else
	DAC960_Info("         %sSynchronous at %d MB/sec\n", Controller,
		    (PhysicalDeviceInfo->NegotiatedDataWidthBits == 16
		     ? "Wide " :""),
		    (PhysicalDeviceInfo->NegotiatedSynchronousMegaTransfers
		     * PhysicalDeviceInfo->NegotiatedDataWidthBits/8));
      if (InquiryUnitSerialNumber->PeripheralDeviceType != 0x1F)
	DAC960_Info("         Serial Number: %s\n", Controller, SerialNumber);
      if (PhysicalDeviceInfo->PhysicalDeviceState ==
	  DAC960_V2_Device_Unconfigured)
	continue;
      DAC960_Info("         Disk Status: %s, %u blocks\n", Controller,
		  (PhysicalDeviceInfo->PhysicalDeviceState
		   == DAC960_V2_Device_Online
		   ? "Online"
		   : PhysicalDeviceInfo->PhysicalDeviceState
		     == DAC960_V2_Device_Rebuild
		     ? "Rebuild"
		     : PhysicalDeviceInfo->PhysicalDeviceState
		       == DAC960_V2_Device_Missing
		       ? "Missing"
		       : PhysicalDeviceInfo->PhysicalDeviceState
			 == DAC960_V2_Device_Critical
			 ? "Critical"
			 : PhysicalDeviceInfo->PhysicalDeviceState
			   == DAC960_V2_Device_Dead
			   ? "Dead"
			   : PhysicalDeviceInfo->PhysicalDeviceState
			     == DAC960_V2_Device_SuspectedDead
			     ? "Suspected-Dead"
			     : PhysicalDeviceInfo->PhysicalDeviceState
			       == DAC960_V2_Device_CommandedOffline
			       ? "Commanded-Offline"
			       : PhysicalDeviceInfo->PhysicalDeviceState
				 == DAC960_V2_Device_Standby
				 ? "Standby" : "Unknown"),
		  PhysicalDeviceInfo->ConfigurableDeviceSize);
      if (PhysicalDeviceInfo->ParityErrors == 0 &&
	  PhysicalDeviceInfo->SoftErrors == 0 &&
	  PhysicalDeviceInfo->HardErrors == 0 &&
	  PhysicalDeviceInfo->MiscellaneousErrors == 0 &&
	  PhysicalDeviceInfo->CommandTimeouts == 0 &&
	  PhysicalDeviceInfo->Retries == 0 &&
	  PhysicalDeviceInfo->Aborts == 0 &&
	  PhysicalDeviceInfo->PredictedFailuresDetected == 0)
	continue;
      DAC960_Info("         Errors - Parity: %d, Soft: %d, "
		  "Hard: %d, Misc: %d\n", Controller,
		  PhysicalDeviceInfo->ParityErrors,
		  PhysicalDeviceInfo->SoftErrors,
		  PhysicalDeviceInfo->HardErrors,
		  PhysicalDeviceInfo->MiscellaneousErrors);
      DAC960_Info("                  Timeouts: %d, Retries: %d, "
		  "Aborts: %d, Predicted: %d\n", Controller,
		  PhysicalDeviceInfo->CommandTimeouts,
		  PhysicalDeviceInfo->Retries,
		  PhysicalDeviceInfo->Aborts,
		  PhysicalDeviceInfo->PredictedFailuresDetected);
    }
  DAC960_Info("  Logical Drives:\n", Controller);
  for (LogicalDriveNumber = 0;
       LogicalDriveNumber < DAC960_MaxLogicalDrives;
       LogicalDriveNumber++)
    {
      DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
	Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
      unsigned char *ReadCacheStatus[] = { "Read Cache Disabled",
					   "Read Cache Enabled",
					   "Read Ahead Enabled",
					   "Intelligent Read Ahead Enabled",
					   "-", "-", "-", "-" };
      unsigned char *WriteCacheStatus[] = { "Write Cache Disabled",
					    "Logical Device Read Only",
					    "Write Cache Enabled",
					    "Intelligent Write Cache Enabled",
					    "-", "-", "-", "-" };
      unsigned char *GeometryTranslation;
      if (LogicalDeviceInfo == NULL) continue;
      switch (LogicalDeviceInfo->DriveGeometry)
	{
	case DAC960_V2_Geometry_128_32:
	  GeometryTranslation = "128/32";
	  break;
	case DAC960_V2_Geometry_255_63:
	  GeometryTranslation = "255/63";
	  break;
	default:
	  GeometryTranslation = "Invalid";
	  DAC960_Error("Illegal Logical Device Geometry %d\n",
		       Controller, LogicalDeviceInfo->DriveGeometry);
	  break;
	}
      DAC960_Info("    /dev/rd/c%dd%d: RAID-%d, %s, %u blocks\n",
		  Controller, Controller->ControllerNumber, LogicalDriveNumber,
		  LogicalDeviceInfo->RAIDLevel,
		  (LogicalDeviceInfo->LogicalDeviceState
		   == DAC960_V2_LogicalDevice_Online
		   ? "Online"
		   : LogicalDeviceInfo->LogicalDeviceState
		     == DAC960_V2_LogicalDevice_Critical
		     ? "Critical" : "Offline"),
		  LogicalDeviceInfo->ConfigurableDeviceSize);
      DAC960_Info("                  Logical Device %s, BIOS Geometry: %s\n",
		  Controller,
		  (LogicalDeviceInfo->LogicalDeviceControl
				     .LogicalDeviceInitialized
		   ? "Initialized" : "Uninitialized"),
		  GeometryTranslation);
      if (LogicalDeviceInfo->StripeSize == 0)
	{
	  if (LogicalDeviceInfo->CacheLineSize == 0)
	    DAC960_Info("                  Stripe Size: N/A, "
			"Segment Size: N/A\n", Controller);
	  else
	    DAC960_Info("                  Stripe Size: N/A, "
			"Segment Size: %dKB\n", Controller,
			1 << (LogicalDeviceInfo->CacheLineSize - 2));
	}
      else
	{
	  if (LogicalDeviceInfo->CacheLineSize == 0)
	    DAC960_Info("                  Stripe Size: %dKB, "
			"Segment Size: N/A\n", Controller,
			1 << (LogicalDeviceInfo->StripeSize - 2));
	  else
	    DAC960_Info("                  Stripe Size: %dKB, "
			"Segment Size: %dKB\n", Controller,
			1 << (LogicalDeviceInfo->StripeSize - 2),
			1 << (LogicalDeviceInfo->CacheLineSize - 2));
	}
      DAC960_Info("                  %s, %s\n", Controller,
		  ReadCacheStatus[
		    LogicalDeviceInfo->LogicalDeviceControl.ReadCache],
		  WriteCacheStatus[
		    LogicalDeviceInfo->LogicalDeviceControl.WriteCache]);
      if (LogicalDeviceInfo->SoftErrors > 0 ||
	  LogicalDeviceInfo->CommandsFailed > 0 ||
	  LogicalDeviceInfo->DeferredWriteErrors)
	DAC960_Info("                  Errors - Soft: %d, Failed: %d, "
		    "Deferred Write: %d\n", Controller,
		    LogicalDeviceInfo->SoftErrors,
		    LogicalDeviceInfo->CommandsFailed,
		    LogicalDeviceInfo->DeferredWriteErrors);

    }
  return true;
}

/*
  DAC960_RegisterBlockDevice registers the Block Device structures
  associated with Controller.
*/

static bool DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller)
{
  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
  int n;

  /*
    Register the Block Device Major Number for this DAC960 Controller.
  */
  if (register_blkdev(MajorNumber, "dac960") < 0)
      return false;

  for (n = 0; n < DAC960_MaxLogicalDrives; n++) {
	struct gendisk *disk = Controller->disks[n];
  	struct request_queue *RequestQueue;

	/* for now, let all request queues share controller's lock */
  	RequestQueue = blk_init_queue(DAC960_RequestFunction,&Controller->queue_lock);
  	if (!RequestQueue) {
		printk("DAC960: failure to allocate request queue\n");
		continue;
  	}
  	Controller->RequestQueue[n] = RequestQueue;
  	blk_queue_bounce_limit(RequestQueue, Controller->BounceBufferLimit);
  	RequestQueue->queuedata = Controller;
  	blk_queue_max_hw_segments(RequestQueue, Controller->DriverScatterGatherLimit);
	blk_queue_max_phys_segments(RequestQueue, Controller->DriverScatterGatherLimit);
	blk_queue_max_sectors(RequestQueue, Controller->MaxBlocksPerCommand);
	disk->queue = RequestQueue;
	sprintf(disk->disk_name, "rd/c%dd%d", Controller->ControllerNumber, n);
	disk->major = MajorNumber;
	disk->first_minor = n << DAC960_MaxPartitionsBits;
	disk->fops = &DAC960_BlockDeviceOperations;
   }
  /*
    Indicate the Block Device Registration completed successfully,
  */
  return true;
}


/*
  DAC960_UnregisterBlockDevice unregisters the Block Device structures
  associated with Controller.
*/

static void DAC960_UnregisterBlockDevice(DAC960_Controller_T *Controller)
{
  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
  int disk;

  /* does order matter when deleting gendisk and cleanup in request queue? */
  for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++) {
	del_gendisk(Controller->disks[disk]);
	blk_cleanup_queue(Controller->RequestQueue[disk]);
	Controller->RequestQueue[disk] = NULL;
  }

  /*
    Unregister the Block Device Major Number for this DAC960 Controller.
  */
  unregister_blkdev(MajorNumber, "dac960");
}

/*
  DAC960_ComputeGenericDiskInfo computes the values for the Generic Disk
  Information Partition Sector Counts and Block Sizes.
*/

static void DAC960_ComputeGenericDiskInfo(DAC960_Controller_T *Controller)
{
	int disk;
	for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++)
		set_capacity(Controller->disks[disk], disk_size(Controller, disk));
}

/*
  DAC960_ReportErrorStatus reports Controller BIOS Messages passed through
  the Error Status Register when the driver performs the BIOS handshaking.
  It returns true for fatal errors and false otherwise.
*/

static bool DAC960_ReportErrorStatus(DAC960_Controller_T *Controller,
					unsigned char ErrorStatus,
					unsigned char Parameter0,
					unsigned char Parameter1)
{
  switch (ErrorStatus)
    {
    case 0x00:
      DAC960_Notice("Physical Device %d:%d Not Responding\n",
		    Controller, Parameter1, Parameter0);
      break;
    case 0x08:
      if (Controller->DriveSpinUpMessageDisplayed) break;
      DAC960_Notice("Spinning Up Drives\n", Controller);
      Controller->DriveSpinUpMessageDisplayed = true;
      break;
    case 0x30:
      DAC960_Notice("Configuration Checksum Error\n", Controller);
      break;
    case 0x60:
      DAC960_Notice("Mirror Race Recovery Failed\n", Controller);
      break;
    case 0x70:
      DAC960_Notice("Mirror Race Recovery In Progress\n", Controller);
      break;
    case 0x90:
      DAC960_Notice("Physical Device %d:%d COD Mismatch\n",
		    Controller, Parameter1, Parameter0);
      break;
    case 0xA0:
      DAC960_Notice("Logical Drive Installation Aborted\n", Controller);
      break;
    case 0xB0:
      DAC960_Notice("Mirror Race On A Critical Logical Drive\n", Controller);
      break;
    case 0xD0:
      DAC960_Notice("New Controller Configuration Found\n", Controller);
      break;
    case 0xF0:
      DAC960_Error("Fatal Memory Parity Error for Controller at\n", Controller);
      return true;
    default:
      DAC960_Error("Unknown Initialization Error %02X for Controller at\n",
		   Controller, ErrorStatus);
      return true;
    }
  return false;
}


/*
 * DAC960_DetectCleanup releases the resources that were allocated
 * during DAC960_DetectController().  DAC960_DetectController can
 * has several internal failure points, so not ALL resources may 
 * have been allocated.  It's important to free only
 * resources that HAVE been allocated.  The code below always
 * tests that the resource has been allocated before attempting to
 * free it.
 */
static void DAC960_DetectCleanup(DAC960_Controller_T *Controller)
{
  int i;

  /* Free the memory mailbox, status, and related structures */
  free_dma_loaf(Controller->PCIDevice, &Controller->DmaPages);
  if (Controller->MemoryMappedAddress) {
  	switch(Controller->HardwareType)
  	{
		case DAC960_GEM_Controller:
			DAC960_GEM_DisableInterrupts(Controller->BaseAddress);
			break;
		case DAC960_BA_Controller:
			DAC960_BA_DisableInterrupts(Controller->BaseAddress);
			break;
		case DAC960_LP_Controller:
			DAC960_LP_DisableInterrupts(Controller->BaseAddress);
			break;
		case DAC960_LA_Controller:
			DAC960_LA_DisableInterrupts(Controller->BaseAddress);
			break;
		case DAC960_PG_Controller:
			DAC960_PG_DisableInterrupts(Controller->BaseAddress);
			break;
		case DAC960_PD_Controller:
			DAC960_PD_DisableInterrupts(Controller->BaseAddress);
			break;
		case DAC960_P_Controller:
			DAC960_PD_DisableInterrupts(Controller->BaseAddress);
			break;
  	}
  	iounmap(Controller->MemoryMappedAddress);
  }
  if (Controller->IRQ_Channel)
  	free_irq(Controller->IRQ_Channel, Controller);
  if (Controller->IO_Address)
	release_region(Controller->IO_Address, 0x80);
  pci_disable_device(Controller->PCIDevice);
  for (i = 0; (i < DAC960_MaxLogicalDrives) && Controller->disks[i]; i++)
       put_disk(Controller->disks[i]);
  DAC960_Controllers[Controller->ControllerNumber] = NULL;
  kfree(Controller);
}


/*
  DAC960_DetectController detects Mylex DAC960/AcceleRAID/eXtremeRAID
  PCI RAID Controllers by interrogating the PCI Configuration Space for
  Controller Type.
*/

static DAC960_Controller_T * 
DAC960_DetectController(struct pci_dev *PCI_Device,
			const struct pci_device_id *entry)
{
  struct DAC960_privdata *privdata =
	  	(struct DAC960_privdata *)entry->driver_data;
  irq_handler_t InterruptHandler = privdata->InterruptHandler;
  unsigned int MemoryWindowSize = privdata->MemoryWindowSize;
  DAC960_Controller_T *Controller = NULL;
  unsigned char DeviceFunction = PCI_Device->devfn;
  unsigned char ErrorStatus, Parameter0, Parameter1;
  unsigned int IRQ_Channel;
  void __iomem *BaseAddress;
  int i;

  Controller = kzalloc(sizeof(DAC960_Controller_T), GFP_ATOMIC);
  if (Controller == NULL) {
	DAC960_Error("Unable to allocate Controller structure for "
                       "Controller at\n", NULL);
	return NULL;
  }
  Controller->ControllerNumber = DAC960_ControllerCount;
  DAC960_Controllers[DAC960_ControllerCount++] = Controller;
  Controller->Bus = PCI_Device->bus->number;
  Controller->FirmwareType = privdata->FirmwareType;
  Controller->HardwareType = privdata->HardwareType;
  Controller->Device = DeviceFunction >> 3;
  Controller->Function = DeviceFunction & 0x7;
  Controller->PCIDevice = PCI_Device;
  strcpy(Controller->FullModelName, "DAC960");

  if (pci_enable_device(PCI_Device))
	goto Failure;

  switch (Controller->HardwareType)
  {
	case DAC960_GEM_Controller:
	  Controller->PCI_Address = pci_resource_start(PCI_Device, 0);
	  break;
	case DAC960_BA_Controller:
	  Controller->PCI_Address = pci_resource_start(PCI_Device, 0);
	  break;
	case DAC960_LP_Controller:
	  Controller->PCI_Address = pci_resource_start(PCI_Device, 0);
	  break;
	case DAC960_LA_Controller:
	  Controller->PCI_Address = pci_resource_start(PCI_Device, 0);
	  break;
	case DAC960_PG_Controller:
	  Controller->PCI_Address = pci_resource_start(PCI_Device, 0);
	  break;
	case DAC960_PD_Controller:
	  Controller->IO_Address = pci_resource_start(PCI_Device, 0);
	  Controller->PCI_Address = pci_resource_start(PCI_Device, 1);
	  break;
	case DAC960_P_Controller:
	  Controller->IO_Address = pci_resource_start(PCI_Device, 0);
	  Controller->PCI_Address = pci_resource_start(PCI_Device, 1);
	  break;
  }

  pci_set_drvdata(PCI_Device, (void *)((long)Controller->ControllerNumber));
  for (i = 0; i < DAC960_MaxLogicalDrives; i++) {
	Controller->disks[i] = alloc_disk(1<<DAC960_MaxPartitionsBits);
	if (!Controller->disks[i])
		goto Failure;
	Controller->disks[i]->private_data = (void *)((long)i);
  }
  init_waitqueue_head(&Controller->CommandWaitQueue);
  init_waitqueue_head(&Controller->HealthStatusWaitQueue);
  spin_lock_init(&Controller->queue_lock);
  DAC960_AnnounceDriver(Controller);
  /*
    Map the Controller Register Window.
  */
 if (MemoryWindowSize < PAGE_SIZE)
	MemoryWindowSize = PAGE_SIZE;
  Controller->MemoryMappedAddress =
	ioremap_nocache(Controller->PCI_Address & PAGE_MASK, MemoryWindowSize);
  Controller->BaseAddress =
	Controller->MemoryMappedAddress + (Controller->PCI_Address & ~PAGE_MASK);
  if (Controller->MemoryMappedAddress == NULL)
  {
	  DAC960_Error("Unable to map Controller Register Window for "
		       "Controller at\n", Controller);
	  goto Failure;
  }
  BaseAddress = Controller->BaseAddress;
  switch (Controller->HardwareType)
  {
	case DAC960_GEM_Controller:
	  DAC960_GEM_DisableInterrupts(BaseAddress);
	  DAC960_GEM_AcknowledgeHardwareMailboxStatus(BaseAddress);
	  udelay(1000);
	  while (DAC960_GEM_InitializationInProgressP(BaseAddress))
	    {
	      if (DAC960_GEM_ReadErrorStatus(BaseAddress, &ErrorStatus,
					    &Parameter0, &Parameter1) &&
		  DAC960_ReportErrorStatus(Controller, ErrorStatus,
					   Parameter0, Parameter1))
		goto Failure;
	      udelay(10);
	    }
	  if (!DAC960_V2_EnableMemoryMailboxInterface(Controller))
	    {
	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
			   "for Controller at\n", Controller);
	      goto Failure;
	    }
	  DAC960_GEM_EnableInterrupts(BaseAddress);
	  Controller->QueueCommand = DAC960_GEM_QueueCommand;
	  Controller->ReadControllerConfiguration =
	    DAC960_V2_ReadControllerConfiguration;
	  Controller->ReadDeviceConfiguration =
	    DAC960_V2_ReadDeviceConfiguration;
	  Controller->ReportDeviceConfiguration =
	    DAC960_V2_ReportDeviceConfiguration;
	  Controller->QueueReadWriteCommand =
	    DAC960_V2_QueueReadWriteCommand;
	  break;
	case DAC960_BA_Controller:
	  DAC960_BA_DisableInterrupts(BaseAddress);
	  DAC960_BA_AcknowledgeHardwareMailboxStatus(BaseAddress);
	  udelay(1000);
	  while (DAC960_BA_InitializationInProgressP(BaseAddress))
	    {
	      if (DAC960_BA_ReadErrorStatus(BaseAddress, &ErrorStatus,
					    &Parameter0, &Parameter1) &&
		  DAC960_ReportErrorStatus(Controller, ErrorStatus,
					   Parameter0, Parameter1))
		goto Failure;
	      udelay(10);
	    }
	  if (!DAC960_V2_EnableMemoryMailboxInterface(Controller))
	    {
	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
			   "for Controller at\n", Controller);
	      goto Failure;
	    }
	  DAC960_BA_EnableInterrupts(BaseAddress);
	  Controller->QueueCommand = DAC960_BA_QueueCommand;
	  Controller->ReadControllerConfiguration =
	    DAC960_V2_ReadControllerConfiguration;
	  Controller->ReadDeviceConfiguration =
	    DAC960_V2_ReadDeviceConfiguration;
	  Controller->ReportDeviceConfiguration =
	    DAC960_V2_ReportDeviceConfiguration;
	  Controller->QueueReadWriteCommand =
	    DAC960_V2_QueueReadWriteCommand;
	  break;
	case DAC960_LP_Controller:
	  DAC960_LP_DisableInterrupts(BaseAddress);
	  DAC960_LP_AcknowledgeHardwareMailboxStatus(BaseAddress);
	  udelay(1000);
	  while (DAC960_LP_InitializationInProgressP(BaseAddress))
	    {
	      if (DAC960_LP_ReadErrorStatus(BaseAddress, &ErrorStatus,
					    &Parameter0, &Parameter1) &&
		  DAC960_ReportErrorStatus(Controller, ErrorStatus,
					   Parameter0, Parameter1))
		goto Failure;
	      udelay(10);
	    }
	  if (!DAC960_V2_EnableMemoryMailboxInterface(Controller))
	    {
	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
			   "for Controller at\n", Controller);
	      goto Failure;
	    }
	  DAC960_LP_EnableInterrupts(BaseAddress);
	  Controller->QueueCommand = DAC960_LP_QueueCommand;
	  Controller->ReadControllerConfiguration =
	    DAC960_V2_ReadControllerConfiguration;
	  Controller->ReadDeviceConfiguration =
	    DAC960_V2_ReadDeviceConfiguration;
	  Controller->ReportDeviceConfiguration =
	    DAC960_V2_ReportDeviceConfiguration;
	  Controller->QueueReadWriteCommand =
	    DAC960_V2_QueueReadWriteCommand;
	  break;
	case DAC960_LA_Controller:
	  DAC960_LA_DisableInterrupts(BaseAddress);
	  DAC960_LA_AcknowledgeHardwareMailboxStatus(BaseAddress);
	  udelay(1000);
	  while (DAC960_LA_InitializationInProgressP(BaseAddress))
	    {
	      if (DAC960_LA_ReadErrorStatus(BaseAddress, &ErrorStatus,
					    &Parameter0, &Parameter1) &&
		  DAC960_ReportErrorStatus(Controller, ErrorStatus,
					   Parameter0, Parameter1))
		goto Failure;
	      udelay(10);
	    }
	  if (!DAC960_V1_EnableMemoryMailboxInterface(Controller))
	    {
	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
			   "for Controller at\n", Controller);
	      goto Failure;
	    }
	  DAC960_LA_EnableInterrupts(BaseAddress);
	  if (Controller->V1.DualModeMemoryMailboxInterface)
	    Controller->QueueCommand = DAC960_LA_QueueCommandDualMode;
	  else Controller->QueueCommand = DAC960_LA_QueueCommandSingleMode;
	  Controller->ReadControllerConfiguration =
	    DAC960_V1_ReadControllerConfiguration;
	  Controller->ReadDeviceConfiguration =
	    DAC960_V1_ReadDeviceConfiguration;
	  Controller->ReportDeviceConfiguration =
	    DAC960_V1_ReportDeviceConfiguration;
	  Controller->QueueReadWriteCommand =
	    DAC960_V1_QueueReadWriteCommand;
	  break;
	case DAC960_PG_Controller:
	  DAC960_PG_DisableInterrupts(BaseAddress);
	  DAC960_PG_AcknowledgeHardwareMailboxStatus(BaseAddress);
	  udelay(1000);
	  while (DAC960_PG_InitializationInProgressP(BaseAddress))
	    {
	      if (DAC960_PG_ReadErrorStatus(BaseAddress, &ErrorStatus,
					    &Parameter0, &Parameter1) &&
		  DAC960_ReportErrorStatus(Controller, ErrorStatus,
					   Parameter0, Parameter1))
		goto Failure;
	      udelay(10);
	    }
	  if (!DAC960_V1_EnableMemoryMailboxInterface(Controller))
	    {
	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
			   "for Controller at\n", Controller);
	      goto Failure;
	    }
	  DAC960_PG_EnableInterrupts(BaseAddress);
	  if (Controller->V1.DualModeMemoryMailboxInterface)
	    Controller->QueueCommand = DAC960_PG_QueueCommandDualMode;
	  else Controller->QueueCommand = DAC960_PG_QueueCommandSingleMode;
	  Controller->ReadControllerConfiguration =
	    DAC960_V1_ReadControllerConfiguration;
	  Controller->ReadDeviceConfiguration =
	    DAC960_V1_ReadDeviceConfiguration;
	  Controller->ReportDeviceConfiguration =
	    DAC960_V1_ReportDeviceConfiguration;
	  Controller->QueueReadWriteCommand =
	    DAC960_V1_QueueReadWriteCommand;
	  break;
	case DAC960_PD_Controller:
	  if (!request_region(Controller->IO_Address, 0x80,
			      Controller->FullModelName)) {
		DAC960_Error("IO port 0x%d busy for Controller at\n",
			     Controller, Controller->IO_Address);
		goto Failure;
	  }
	  DAC960_PD_DisableInterrupts(BaseAddress);
	  DAC960_PD_AcknowledgeStatus(BaseAddress);
	  udelay(1000);
	  while (DAC960_PD_InitializationInProgressP(BaseAddress))
	    {
	      if (DAC960_PD_ReadErrorStatus(BaseAddress, &ErrorStatus,
					    &Parameter0, &Parameter1) &&
		  DAC960_ReportErrorStatus(Controller, ErrorStatus,
					   Parameter0, Parameter1))
		goto Failure;
	      udelay(10);
	    }
	  if (!DAC960_V1_EnableMemoryMailboxInterface(Controller))
	    {
	      DAC960_Error("Unable to allocate DMA mapped memory "
			   "for Controller at\n", Controller);
	      goto Failure;
	    }
	  DAC960_PD_EnableInterrupts(BaseAddress);
	  Controller->QueueCommand = DAC960_PD_QueueCommand;
	  Controller->ReadControllerConfiguration =
	    DAC960_V1_ReadControllerConfiguration;
	  Controller->ReadDeviceConfiguration =
	    DAC960_V1_ReadDeviceConfiguration;
	  Controller->ReportDeviceConfiguration =
	    DAC960_V1_ReportDeviceConfiguration;
	  Controller->QueueReadWriteCommand =
	    DAC960_V1_QueueReadWriteCommand;
	  break;
	case DAC960_P_Controller:
	  if (!request_region(Controller->IO_Address, 0x80,
			      Controller->FullModelName)){
		DAC960_Error("IO port 0x%d busy for Controller at\n",
		   	     Controller, Controller->IO_Address);
		goto Failure;
	  }
	  DAC960_PD_DisableInterrupts(BaseAddress);
	  DAC960_PD_AcknowledgeStatus(BaseAddress);
	  udelay(1000);
	  while (DAC960_PD_InitializationInProgressP(BaseAddress))
	    {
	      if (DAC960_PD_ReadErrorStatus(BaseAddress, &ErrorStatus,
					    &Parameter0, &Parameter1) &&
		  DAC960_ReportErrorStatus(Controller, ErrorStatus,
					   Parameter0, Parameter1))
		goto Failure;
	      udelay(10);
	    }
	  if (!DAC960_V1_EnableMemoryMailboxInterface(Controller))
	    {
	      DAC960_Error("Unable to allocate DMA mapped memory"
			   "for Controller at\n", Controller);
	      goto Failure;
	    }
	  DAC960_PD_EnableInterrupts(BaseAddress);
	  Controller->QueueCommand = DAC960_P_QueueCommand;
	  Controller->ReadControllerConfiguration =
	    DAC960_V1_ReadControllerConfiguration;
	  Controller->ReadDeviceConfiguration =
	    DAC960_V1_ReadDeviceConfiguration;
	  Controller->ReportDeviceConfiguration =
	    DAC960_V1_ReportDeviceConfiguration;
	  Controller->QueueReadWriteCommand =
	    DAC960_V1_QueueReadWriteCommand;
	  break;
  }
  /*
     Acquire shared access to the IRQ Channel.
  */
  IRQ_Channel = PCI_Device->irq;
  if (request_irq(IRQ_Channel, InterruptHandler, IRQF_SHARED,
		      Controller->FullModelName, Controller) < 0)
  {
	DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n",
		       Controller, Controller->IRQ_Channel);
	goto Failure;
  }
  Controller->IRQ_Channel = IRQ_Channel;
  Controller->InitialCommand.CommandIdentifier = 1;
  Controller->InitialCommand.Controller = Controller;
  Controller->Commands[0] = &Controller->InitialCommand;
  Controller->FreeCommands = &Controller->InitialCommand;
  return Controller;
      
Failure:
  if (Controller->IO_Address == 0)
	DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A "
		     "PCI Address 0x%X\n", Controller,
		     Controller->Bus, Controller->Device,
		     Controller->Function, Controller->PCI_Address);
  else
	DAC960_Error("PCI Bus %d Device %d Function %d I/O Address "
			"0x%X PCI Address 0x%X\n", Controller,
			Controller->Bus, Controller->Device,
			Controller->Function, Controller->IO_Address,
			Controller->PCI_Address);
  DAC960_DetectCleanup(Controller);
  DAC960_ControllerCount--;
  return NULL;
}

/*
  DAC960_InitializeController initializes Controller.
*/

static bool 
DAC960_InitializeController(DAC960_Controller_T *Controller)
{
  if (DAC960_ReadControllerConfiguration(Controller) &&
      DAC960_ReportControllerConfiguration(Controller) &&
      DAC960_CreateAuxiliaryStructures(Controller) &&
      DAC960_ReadDeviceConfiguration(Controller) &&
      DAC960_ReportDeviceConfiguration(Controller) &&
      DAC960_RegisterBlockDevice(Controller))
    {
      /*
	Initialize the Monitoring Timer.
      */
      init_timer(&Controller->MonitoringTimer);
      Controller->MonitoringTimer.expires =
	jiffies + DAC960_MonitoringTimerInterval;
      Controller->MonitoringTimer.data = (unsigned long) Controller;
      Controller->MonitoringTimer.function = DAC960_MonitoringTimerFunction;
      add_timer(&Controller->MonitoringTimer);
      Controller->ControllerInitialized = true;
      return true;
    }
  return false;
}


/*
  DAC960_FinalizeController finalizes Controller.
*/

static void DAC960_FinalizeController(DAC960_Controller_T *Controller)
{
  if (Controller->ControllerInitialized)
    {
      unsigned long flags;

      /*
       * Acquiring and releasing lock here eliminates
       * a very low probability race.
       *
       * The code below allocates controller command structures
       * from the free list without holding the controller lock.
       * This is safe assuming there is no other activity on
       * the controller at the time.
       * 
       * But, there might be a monitoring command still
       * in progress.  Setting the Shutdown flag while holding
       * the lock ensures that there is no monitoring command
       * in the interrupt handler currently, and any monitoring
       * commands that complete from this time on will NOT return
       * their command structure to the free list.
       */

      spin_lock_irqsave(&Controller->queue_lock, flags);
      Controller->ShutdownMonitoringTimer = 1;
      spin_unlock_irqrestore(&Controller->queue_lock, flags);

      del_timer_sync(&Controller->MonitoringTimer);
      if (Controller->FirmwareType == DAC960_V1_Controller)
	{
	  DAC960_Notice("Flushing Cache...", Controller);
	  DAC960_V1_ExecuteType3(Controller, DAC960_V1_Flush, 0);
	  DAC960_Notice("done\n", Controller);

	  if (Controller->HardwareType == DAC960_PD_Controller)
	      release_region(Controller->IO_Address, 0x80);
	}
      else
	{
	  DAC960_Notice("Flushing Cache...", Controller);
	  DAC960_V2_DeviceOperation(Controller, DAC960_V2_PauseDevice,
				    DAC960_V2_RAID_Controller);
	  DAC960_Notice("done\n", Controller);
	}
    }
  DAC960_UnregisterBlockDevice(Controller);
  DAC960_DestroyAuxiliaryStructures(Controller);
  DAC960_DestroyProcEntries(Controller);
  DAC960_DetectCleanup(Controller);
}


/*
  DAC960_Probe verifies controller's existence and
  initializes the DAC960 Driver for that controller.
*/

static int 
DAC960_Probe(struct pci_dev *dev, const struct pci_device_id *entry)
{
  int disk;
  DAC960_Controller_T *Controller;

  if (DAC960_ControllerCount == DAC960_MaxControllers)
  {
	DAC960_Error("More than %d DAC960 Controllers detected - "
                       "ignoring from Controller at\n",
                       NULL, DAC960_MaxControllers);
	return -ENODEV;
  }

  Controller = DAC960_DetectController(dev, entry);
  if (!Controller)
	return -ENODEV;

  if (!DAC960_InitializeController(Controller)) {
  	DAC960_FinalizeController(Controller);
	return -ENODEV;
  }

  for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++) {
        set_capacity(Controller->disks[disk], disk_size(Controller, disk));
        add_disk(Controller->disks[disk]);
  }
  DAC960_CreateProcEntries(Controller);
  return 0;
}


/*
  DAC960_Finalize finalizes the DAC960 Driver.
*/

static void DAC960_Remove(struct pci_dev *PCI_Device)
{
  int Controller_Number = (long)pci_get_drvdata(PCI_Device);
  DAC960_Controller_T *Controller = DAC960_Controllers[Controller_Number];
  if (Controller != NULL)
      DAC960_FinalizeController(Controller);
}


/*
  DAC960_V1_QueueReadWriteCommand prepares and queues a Read/Write Command for
  DAC960 V1 Firmware Controllers.
*/

static void DAC960_V1_QueueReadWriteCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  DAC960_V1_ScatterGatherSegment_T *ScatterGatherList =
					Command->V1.ScatterGatherList;
  struct scatterlist *ScatterList = Command->V1.ScatterList;

  DAC960_V1_ClearCommand(Command);

  if (Command->SegmentCount == 1)
    {
      if (Command->DmaDirection == PCI_DMA_FROMDEVICE)
	CommandMailbox->Type5.CommandOpcode = DAC960_V1_Read;
      else 
        CommandMailbox->Type5.CommandOpcode = DAC960_V1_Write;

      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
      CommandMailbox->Type5.BusAddress =
			(DAC960_BusAddress32_T)sg_dma_address(ScatterList);	
    }
  else
    {
      int i;

      if (Command->DmaDirection == PCI_DMA_FROMDEVICE)
	CommandMailbox->Type5.CommandOpcode = DAC960_V1_ReadWithScatterGather;
      else
	CommandMailbox->Type5.CommandOpcode = DAC960_V1_WriteWithScatterGather;

      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
      CommandMailbox->Type5.BusAddress = Command->V1.ScatterGatherListDMA;

      CommandMailbox->Type5.ScatterGatherCount = Command->SegmentCount;

      for (i = 0; i < Command->SegmentCount; i++, ScatterList++, ScatterGatherList++) {
		ScatterGatherList->SegmentDataPointer =
			(DAC960_BusAddress32_T)sg_dma_address(ScatterList);
		ScatterGatherList->SegmentByteCount =
			(DAC960_ByteCount32_T)sg_dma_len(ScatterList);
      }
    }
  DAC960_QueueCommand(Command);
}


/*
  DAC960_V2_QueueReadWriteCommand prepares and queues a Read/Write Command for
  DAC960 V2 Firmware Controllers.
*/

static void DAC960_V2_QueueReadWriteCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  struct scatterlist *ScatterList = Command->V2.ScatterList;

  DAC960_V2_ClearCommand(Command);

  CommandMailbox->SCSI_10.CommandOpcode = DAC960_V2_SCSI_10;
  CommandMailbox->SCSI_10.CommandControlBits.DataTransferControllerToHost =
    (Command->DmaDirection == PCI_DMA_FROMDEVICE);
  CommandMailbox->SCSI_10.DataTransferSize =
    Command->BlockCount << DAC960_BlockSizeBits;
  CommandMailbox->SCSI_10.RequestSenseBusAddress = Command->V2.RequestSenseDMA;
  CommandMailbox->SCSI_10.PhysicalDevice =
    Controller->V2.LogicalDriveToVirtualDevice[Command->LogicalDriveNumber];
  CommandMailbox->SCSI_10.RequestSenseSize = sizeof(DAC960_SCSI_RequestSense_T);
  CommandMailbox->SCSI_10.CDBLength = 10;
  CommandMailbox->SCSI_10.SCSI_CDB[0] =
    (Command->DmaDirection == PCI_DMA_FROMDEVICE ? 0x28 : 0x2A);
  CommandMailbox->SCSI_10.SCSI_CDB[2] = Command->BlockNumber >> 24;
  CommandMailbox->SCSI_10.SCSI_CDB[3] = Command->BlockNumber >> 16;
  CommandMailbox->SCSI_10.SCSI_CDB[4] = Command->BlockNumber >> 8;
  CommandMailbox->SCSI_10.SCSI_CDB[5] = Command->BlockNumber;
  CommandMailbox->SCSI_10.SCSI_CDB[7] = Command->BlockCount >> 8;
  CommandMailbox->SCSI_10.SCSI_CDB[8] = Command->BlockCount;

  if (Command->SegmentCount == 1)
    {
      CommandMailbox->SCSI_10.DataTransferMemoryAddress
			     .ScatterGatherSegments[0]
			     .SegmentDataPointer =
	(DAC960_BusAddress64_T)sg_dma_address(ScatterList);
      CommandMailbox->SCSI_10.DataTransferMemoryAddress
			     .ScatterGatherSegments[0]
			     .SegmentByteCount =
	CommandMailbox->SCSI_10.DataTransferSize;
    }
  else
    {
      DAC960_V2_ScatterGatherSegment_T *ScatterGatherList;
      int i;

      if (Command->SegmentCount > 2)
	{
          ScatterGatherList = Command->V2.ScatterGatherList;
	  CommandMailbox->SCSI_10.CommandControlBits
			 .AdditionalScatterGatherListMemory = true;
	  CommandMailbox->SCSI_10.DataTransferMemoryAddress
		.ExtendedScatterGather.ScatterGatherList0Length = Command->SegmentCount;
	  CommandMailbox->SCSI_10.DataTransferMemoryAddress
			 .ExtendedScatterGather.ScatterGatherList0Address =
	    Command->V2.ScatterGatherListDMA;
	}
      else
	ScatterGatherList = CommandMailbox->SCSI_10.DataTransferMemoryAddress
				 .ScatterGatherSegments;

      for (i = 0; i < Command->SegmentCount; i++, ScatterList++, ScatterGatherList++) {
		ScatterGatherList->SegmentDataPointer =
			(DAC960_BusAddress64_T)sg_dma_address(ScatterList);
		ScatterGatherList->SegmentByteCount =
			(DAC960_ByteCount64_T)sg_dma_len(ScatterList);
      }
    }
  DAC960_QueueCommand(Command);
}


static int DAC960_process_queue(DAC960_Controller_T *Controller, struct request_queue *req_q)
{
	struct request *Request;
	DAC960_Command_T *Command;

   while(1) {
	Request = elv_next_request(req_q);
	if (!Request)
		return 1;

	Command = DAC960_AllocateCommand(Controller);
	if (Command == NULL)
		return 0;

	if (rq_data_dir(Request) == READ) {
		Command->DmaDirection = PCI_DMA_FROMDEVICE;
		Command->CommandType = DAC960_ReadCommand;
	} else {
		Command->DmaDirection = PCI_DMA_TODEVICE;
		Command->CommandType = DAC960_WriteCommand;
	}
	Command->Completion = Request->end_io_data;
	Command->LogicalDriveNumber = (long)Request->rq_disk->private_data;
	Command->BlockNumber = Request->sector;
	Command->BlockCount = Request->nr_sectors;
	Command->Request = Request;
	blkdev_dequeue_request(Request);
	Command->SegmentCount = blk_rq_map_sg(req_q,
		  Command->Request, Command->cmd_sglist);
	/* pci_map_sg MAY change the value of SegCount */
	Command->SegmentCount = pci_map_sg(Controller->PCIDevice, Command->cmd_sglist,
		 Command->SegmentCount, Command->DmaDirection);

	DAC960_QueueReadWriteCommand(Command);
  }
}

/*
  DAC960_ProcessRequest attempts to remove one I/O Request from Controller's
  I/O Request Queue and queues it to the Controller.  WaitForCommand is true if
  this function should wait for a Command to become available if necessary.
  This function returns true if an I/O Request was queued and false otherwise.
*/
static void DAC960_ProcessRequest(DAC960_Controller_T *controller)
{
	int i;

	if (!controller->ControllerInitialized)
		return;

	/* Do this better later! */
	for (i = controller->req_q_index; i < DAC960_MaxLogicalDrives; i++) {
		struct request_queue *req_q = controller->RequestQueue[i];

		if (req_q == NULL)
			continue;

		if (!DAC960_process_queue(controller, req_q)) {
			controller->req_q_index = i;
			return;
		}
	}

	if (controller->req_q_index == 0)
		return;

	for (i = 0; i < controller->req_q_index; i++) {
		struct request_queue *req_q = controller->RequestQueue[i];

		if (req_q == NULL)
			continue;

		if (!DAC960_process_queue(controller, req_q)) {
			controller->req_q_index = i;
			return;
		}
	}
}


/*
  DAC960_queue_partial_rw extracts one bio from the request already
  associated with argument command, and construct a new command block to retry I/O
  only on that bio.  Queue that command to the controller.

  This function re-uses a previously-allocated Command,
  	there is no failure mode from trying to allocate a command.
*/

static void DAC960_queue_partial_rw(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  struct request *Request = Command->Request;
  struct request_queue *req_q = Controller->RequestQueue[Command->LogicalDriveNumber];

  if (Command->DmaDirection == PCI_DMA_FROMDEVICE)
    Command->CommandType = DAC960_ReadRetryCommand;
  else
    Command->CommandType = DAC960_WriteRetryCommand;

  /*
   * We could be more efficient with these mapping requests
   * and map only the portions that we need.  But since this
   * code should almost never be called, just go with a
   * simple coding.
   */
  (void)blk_rq_map_sg(req_q, Command->Request, Command->cmd_sglist);

  (void)pci_map_sg(Controller->PCIDevice, Command->cmd_sglist, 1, Command->DmaDirection);
  /*
   * Resubmitting the request sector at a time is really tedious.
   * But, this should almost never happen.  So, we're willing to pay
   * this price so that in the end, as much of the transfer is completed
   * successfully as possible.
   */
  Command->SegmentCount = 1;
  Command->BlockNumber = Request->sector;
  Command->BlockCount = 1;
  DAC960_QueueReadWriteCommand(Command);
  return;
}

/*
  DAC960_RequestFunction is the I/O Request Function for DAC960 Controllers.
*/

static void DAC960_RequestFunction(struct request_queue *RequestQueue)
{
	DAC960_ProcessRequest(RequestQueue->queuedata);
}

/*
  DAC960_ProcessCompletedBuffer performs completion processing for an
  individual Buffer.
*/

static inline bool DAC960_ProcessCompletedRequest(DAC960_Command_T *Command,
						 bool SuccessfulIO)
{
	struct request *Request = Command->Request;
	int UpToDate;

	UpToDate = 0;
	if (SuccessfulIO)
		UpToDate = 1;

	pci_unmap_sg(Command->Controller->PCIDevice, Command->cmd_sglist,
		Command->SegmentCount, Command->DmaDirection);

	 if (!end_that_request_first(Request, UpToDate, Command->BlockCount)) {
		add_disk_randomness(Request->rq_disk);
 	 	end_that_request_last(Request, UpToDate);

		if (Command->Completion) {
			complete(Command->Completion);
			Command->Completion = NULL;
		}
		return true;
	}
	return false;
}

/*
  DAC960_V1_ReadWriteError prints an appropriate error message for Command
  when an error occurs on a Read or Write operation.
*/

static void DAC960_V1_ReadWriteError(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  unsigned char *CommandName = "UNKNOWN";
  switch (Command->CommandType)
    {
    case DAC960_ReadCommand:
    case DAC960_ReadRetryCommand:
      CommandName = "READ";
      break;
    case DAC960_WriteCommand:
    case DAC960_WriteRetryCommand:
      CommandName = "WRITE";
      break;
    case DAC960_MonitoringCommand:
    case DAC960_ImmediateCommand:
    case DAC960_QueuedCommand:
      break;
    }
  switch (Command->V1.CommandStatus)
    {
    case DAC960_V1_IrrecoverableDataError:
      DAC960_Error("Irrecoverable Data Error on %s:\n",
		   Controller, CommandName);
      break;
    case DAC960_V1_LogicalDriveNonexistentOrOffline:
      DAC960_Error("Logical Drive Nonexistent or Offline on %s:\n",
		   Controller, CommandName);
      break;
    case DAC960_V1_AccessBeyondEndOfLogicalDrive:
      DAC960_Error("Attempt to Access Beyond End of Logical Drive "
		   "on %s:\n", Controller, CommandName);
      break;
    case DAC960_V1_BadDataEncountered:
      DAC960_Error("Bad Data Encountered on %s:\n", Controller, CommandName);
      break;
    default:
      DAC960_Error("Unexpected Error Status %04X on %s:\n",
		   Controller, Command->V1.CommandStatus, CommandName);
      break;
    }
  DAC960_Error("  /dev/rd/c%dd%d:   absolute blocks %u..%u\n",
	       Controller, Controller->ControllerNumber,
	       Command->LogicalDriveNumber, Command->BlockNumber,
	       Command->BlockNumber + Command->BlockCount - 1);
}


/*
  DAC960_V1_ProcessCompletedCommand performs completion processing for Command
  for DAC960 V1 Firmware Controllers.
*/

static void DAC960_V1_ProcessCompletedCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  DAC960_CommandType_T CommandType = Command->CommandType;
  DAC960_V1_CommandOpcode_T CommandOpcode =
    Command->V1.CommandMailbox.Common.CommandOpcode;
  DAC960_V1_CommandStatus_T CommandStatus = Command->V1.CommandStatus;

  if (CommandType == DAC960_ReadCommand ||
      CommandType == DAC960_WriteCommand)
    {

#ifdef FORCE_RETRY_DEBUG
      CommandStatus = DAC960_V1_IrrecoverableDataError;
#endif

      if (CommandStatus == DAC960_V1_NormalCompletion) {

		if (!DAC960_ProcessCompletedRequest(Command, true))
			BUG();

      } else if (CommandStatus == DAC960_V1_IrrecoverableDataError ||
		CommandStatus == DAC960_V1_BadDataEncountered)
	{
	  /*
	   * break the command down into pieces and resubmit each
	   * piece, hoping that some of them will succeed.
	   */
	   DAC960_queue_partial_rw(Command);
	   return;
	}
      else
	{
	  if (CommandStatus != DAC960_V1_LogicalDriveNonexistentOrOffline)
	    DAC960_V1_ReadWriteError(Command);

	 if (!DAC960_ProcessCompletedRequest(Command, false))
		BUG();
	}
    }
  else if (CommandType == DAC960_ReadRetryCommand ||
	   CommandType == DAC960_WriteRetryCommand)
    {
      bool normal_completion;
#ifdef FORCE_RETRY_FAILURE_DEBUG
      static int retry_count = 1;
#endif
      /*
        Perform completion processing for the portion that was
        retried, and submit the next portion, if any.
      */
      normal_completion = true;
      if (CommandStatus != DAC960_V1_NormalCompletion) {
        normal_completion = false;
        if (CommandStatus != DAC960_V1_LogicalDriveNonexistentOrOffline)
            DAC960_V1_ReadWriteError(Command);
      }

#ifdef FORCE_RETRY_FAILURE_DEBUG
      if (!(++retry_count % 10000)) {
	      printk("V1 error retry failure test\n");
	      normal_completion = false;
              DAC960_V1_ReadWriteError(Command);
      }
#endif

      if (!DAC960_ProcessCompletedRequest(Command, normal_completion)) {
        DAC960_queue_partial_rw(Command);
        return;
      }
    }

  else if (CommandType == DAC960_MonitoringCommand)
    {
      if (Controller->ShutdownMonitoringTimer)
	      return;
      if (CommandOpcode == DAC960_V1_Enquiry)
	{
	  DAC960_V1_Enquiry_T *OldEnquiry = &Controller->V1.Enquiry;
	  DAC960_V1_Enquiry_T *NewEnquiry = Controller->V1.NewEnquiry;
	  unsigned int OldCriticalLogicalDriveCount =
	    OldEnquiry->CriticalLogicalDriveCount;
	  unsigned int NewCriticalLogicalDriveCount =
	    NewEnquiry->CriticalLogicalDriveCount;
	  if (NewEnquiry->NumberOfLogicalDrives > Controller->LogicalDriveCount)
	    {
	      int LogicalDriveNumber = Controller->LogicalDriveCount - 1;
	      while (++LogicalDriveNumber < NewEnquiry->NumberOfLogicalDrives)
		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
				"Now Exists\n", Controller,
				LogicalDriveNumber,
				Controller->ControllerNumber,
				LogicalDriveNumber);
	      Controller->LogicalDriveCount = NewEnquiry->NumberOfLogicalDrives;
	      DAC960_ComputeGenericDiskInfo(Controller);
	    }
	  if (NewEnquiry->NumberOfLogicalDrives < Controller->LogicalDriveCount)
	    {
	      int LogicalDriveNumber = NewEnquiry->NumberOfLogicalDrives - 1;
	      while (++LogicalDriveNumber < Controller->LogicalDriveCount)
		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
				"No Longer Exists\n", Controller,
				LogicalDriveNumber,
				Controller->ControllerNumber,
				LogicalDriveNumber);
	      Controller->LogicalDriveCount = NewEnquiry->NumberOfLogicalDrives;
	      DAC960_ComputeGenericDiskInfo(Controller);
	    }
	  if (NewEnquiry->StatusFlags.DeferredWriteError !=
	      OldEnquiry->StatusFlags.DeferredWriteError)
	    DAC960_Critical("Deferred Write Error Flag is now %s\n", Controller,
			    (NewEnquiry->StatusFlags.DeferredWriteError
			     ? "TRUE" : "FALSE"));
	  if ((NewCriticalLogicalDriveCount > 0 ||
	       NewCriticalLogicalDriveCount != OldCriticalLogicalDriveCount) ||
	      (NewEnquiry->OfflineLogicalDriveCount > 0 ||
	       NewEnquiry->OfflineLogicalDriveCount !=
	       OldEnquiry->OfflineLogicalDriveCount) ||
	      (NewEnquiry->DeadDriveCount > 0 ||
	       NewEnquiry->DeadDriveCount !=
	       OldEnquiry->DeadDriveCount) ||
	      (NewEnquiry->EventLogSequenceNumber !=
	       OldEnquiry->EventLogSequenceNumber) ||
	      Controller->MonitoringTimerCount == 0 ||
	      time_after_eq(jiffies, Controller->SecondaryMonitoringTime
	       + DAC960_SecondaryMonitoringInterval))
	    {
	      Controller->V1.NeedLogicalDriveInformation = true;
	      Controller->V1.NewEventLogSequenceNumber =
		NewEnquiry->EventLogSequenceNumber;
	      Controller->V1.NeedErrorTableInformation = true;
	      Controller->V1.NeedDeviceStateInformation = true;
	      Controller->V1.StartDeviceStateScan = true;
	      Controller->V1.NeedBackgroundInitializationStatus =
		Controller->V1.BackgroundInitializationStatusSupported;
	      Controller->SecondaryMonitoringTime = jiffies;
	    }
	  if (NewEnquiry->RebuildFlag == DAC960_V1_StandbyRebuildInProgress ||
	      NewEnquiry->RebuildFlag
	      == DAC960_V1_BackgroundRebuildInProgress ||
	      OldEnquiry->RebuildFlag == DAC960_V1_StandbyRebuildInProgress ||
	      OldEnquiry->RebuildFlag == DAC960_V1_BackgroundRebuildInProgress)
	    {
	      Controller->V1.NeedRebuildProgress = true;
	      Controller->V1.RebuildProgressFirst =
		(NewEnquiry->CriticalLogicalDriveCount <
		 OldEnquiry->CriticalLogicalDriveCount);
	    }
	  if (OldEnquiry->RebuildFlag == DAC960_V1_BackgroundCheckInProgress)
	    switch (NewEnquiry->RebuildFlag)
	      {
	      case DAC960_V1_NoStandbyRebuildOrCheckInProgress:
		DAC960_Progress("Consistency Check Completed Successfully\n",
				Controller);
		break;
	      case DAC960_V1_StandbyRebuildInProgress:
	      case DAC960_V1_BackgroundRebuildInProgress:
		break;
	      case DAC960_V1_BackgroundCheckInProgress:
		Controller->V1.NeedConsistencyCheckProgress = true;
		break;
	      case DAC960_V1_StandbyRebuildCompletedWithError:
		DAC960_Progress("Consistency Check Completed with Error\n",
				Controller);
		break;
	      case DAC960_V1_BackgroundRebuildOrCheckFailed_DriveFailed:
		DAC960_Progress("Consistency Check Failed - "
				"Physical Device Failed\n", Controller);
		break;
	      case DAC960_V1_BackgroundRebuildOrCheckFailed_LogicalDriveFailed:
		DAC960_Progress("Consistency Check Failed - "
				"Logical Drive Failed\n", Controller);
		break;
	      case DAC960_V1_BackgroundRebuildOrCheckFailed_OtherCauses:
		DAC960_Progress("Consistency Check Failed - Other Causes\n",
				Controller);
		break;
	      case DAC960_V1_BackgroundRebuildOrCheckSuccessfullyTerminated:
		DAC960_Progress("Consistency Check Successfully Terminated\n",
				Controller);
		break;
	      }
	  else if (NewEnquiry->RebuildFlag
		   == DAC960_V1_BackgroundCheckInProgress)
	    Controller->V1.NeedConsistencyCheckProgress = true;
	  Controller->MonitoringAlertMode =
	    (NewEnquiry->CriticalLogicalDriveCount > 0 ||
	     NewEnquiry->OfflineLogicalDriveCount > 0 ||
	     NewEnquiry->DeadDriveCount > 0);
	  if (NewEnquiry->RebuildFlag > DAC960_V1_BackgroundCheckInProgress)
	    {
	      Controller->V1.PendingRebuildFlag = NewEnquiry->RebuildFlag;
	      Controller->V1.RebuildFlagPending = true;
	    }
	  memcpy(&Controller->V1.Enquiry, &Controller->V1.NewEnquiry,
		 sizeof(DAC960_V1_Enquiry_T));
	}
      else if (CommandOpcode == DAC960_V1_PerformEventLogOperation)
	{
	  static char
	    *DAC960_EventMessages[] =
	       { "killed because write recovery failed",
		 "killed because of SCSI bus reset failure",
		 "killed because of double check condition",
		 "killed because it was removed",
		 "killed because of gross error on SCSI chip",
		 "killed because of bad tag returned from drive",
		 "killed because of timeout on SCSI command",
		 "killed because of reset SCSI command issued from system",
		 "killed because busy or parity error count exceeded limit",
		 "killed because of 'kill drive' command from system",
		 "killed because of selection timeout",
		 "killed due to SCSI phase sequence error",
		 "killed due to unknown status" };
	  DAC960_V1_EventLogEntry_T *EventLogEntry =
	    	Controller->V1.EventLogEntry;
	  if (EventLogEntry->SequenceNumber ==
	      Controller->V1.OldEventLogSequenceNumber)
	    {
	      unsigned char SenseKey = EventLogEntry->SenseKey;
	      unsigned char AdditionalSenseCode =
		EventLogEntry->AdditionalSenseCode;
	      unsigned char AdditionalSenseCodeQualifier =
		EventLogEntry->AdditionalSenseCodeQualifier;
	      if (SenseKey == DAC960_SenseKey_VendorSpecific &&
		  AdditionalSenseCode == 0x80 &&
		  AdditionalSenseCodeQualifier <
		  ARRAY_SIZE(DAC960_EventMessages))
		DAC960_Critical("Physical Device %d:%d %s\n", Controller,
				EventLogEntry->Channel,
				EventLogEntry->TargetID,
				DAC960_EventMessages[
				  AdditionalSenseCodeQualifier]);
	      else if (SenseKey == DAC960_SenseKey_UnitAttention &&
		       AdditionalSenseCode == 0x29)
		{
		  if (Controller->MonitoringTimerCount > 0)
		    Controller->V1.DeviceResetCount[EventLogEntry->Channel]
						   [EventLogEntry->TargetID]++;
		}
	      else if (!(SenseKey == DAC960_SenseKey_NoSense ||
			 (SenseKey == DAC960_SenseKey_NotReady &&
			  AdditionalSenseCode == 0x04 &&
			  (AdditionalSenseCodeQualifier == 0x01 ||
			   AdditionalSenseCodeQualifier == 0x02))))
		{
		  DAC960_Critical("Physical Device %d:%d Error Log: "
				  "Sense Key = %X, ASC = %02X, ASCQ = %02X\n",
				  Controller,
				  EventLogEntry->Channel,
				  EventLogEntry->TargetID,
				  SenseKey,
				  AdditionalSenseCode,
				  AdditionalSenseCodeQualifier);
		  DAC960_Critical("Physical Device %d:%d Error Log: "
				  "Information = %02X%02X%02X%02X "
				  "%02X%02X%02X%02X\n",
				  Controller,
				  EventLogEntry->Channel,
				  EventLogEntry->TargetID,
				  EventLogEntry->Information[0],
				  EventLogEntry->Information[1],
				  EventLogEntry->Information[2],
				  EventLogEntry->Information[3],
				  EventLogEntry->CommandSpecificInformation[0],
				  EventLogEntry->CommandSpecificInformation[1],
				  EventLogEntry->CommandSpecificInformation[2],
				  EventLogEntry->CommandSpecificInformation[3]);
		}
	    }
	  Controller->V1.OldEventLogSequenceNumber++;
	}
      else if (CommandOpcode == DAC960_V1_GetErrorTable)
	{
	  DAC960_V1_ErrorTable_T *OldErrorTable = &Controller->V1.ErrorTable;
	  DAC960_V1_ErrorTable_T *NewErrorTable = Controller->V1.NewErrorTable;
	  int Channel, TargetID;
	  for (Channel = 0; Channel < Controller->Channels; Channel++)
	    for (TargetID = 0; TargetID < Controller->Targets; TargetID++)
	      {
		DAC960_V1_ErrorTableEntry_T *NewErrorEntry =
		  &NewErrorTable->ErrorTableEntries[Channel][TargetID];
		DAC960_V1_ErrorTableEntry_T *OldErrorEntry =
		  &OldErrorTable->ErrorTableEntries[Channel][TargetID];
		if ((NewErrorEntry->ParityErrorCount !=
		     OldErrorEntry->ParityErrorCount) ||
		    (NewErrorEntry->SoftErrorCount !=
		     OldErrorEntry->SoftErrorCount) ||
		    (NewErrorEntry->HardErrorCount !=
		     OldErrorEntry->HardErrorCount) ||
		    (NewErrorEntry->MiscErrorCount !=
		     OldErrorEntry->MiscErrorCount))
		  DAC960_Critical("Physical Device %d:%d Errors: "
				  "Parity = %d, Soft = %d, "
				  "Hard = %d, Misc = %d\n",
				  Controller, Channel, TargetID,
				  NewErrorEntry->ParityErrorCount,
				  NewErrorEntry->SoftErrorCount,
				  NewErrorEntry->HardErrorCount,
				  NewErrorEntry->MiscErrorCount);
	      }
	  memcpy(&Controller->V1.ErrorTable, Controller->V1.NewErrorTable,
		 sizeof(DAC960_V1_ErrorTable_T));
	}
      else if (CommandOpcode == DAC960_V1_GetDeviceState)
	{
	  DAC960_V1_DeviceState_T *OldDeviceState =
	    &Controller->V1.DeviceState[Controller->V1.DeviceStateChannel]
				       [Controller->V1.DeviceStateTargetID];
	  DAC960_V1_DeviceState_T *NewDeviceState =
	    Controller->V1.NewDeviceState;
	  if (NewDeviceState->DeviceState != OldDeviceState->DeviceState)
	    DAC960_Critical("Physical Device %d:%d is now %s\n", Controller,
			    Controller->V1.DeviceStateChannel,
			    Controller->V1.DeviceStateTargetID,
			    (NewDeviceState->DeviceState
			     == DAC960_V1_Device_Dead
			     ? "DEAD"
			     : NewDeviceState->DeviceState
			       == DAC960_V1_Device_WriteOnly
			       ? "WRITE-ONLY"
			       : NewDeviceState->DeviceState
				 == DAC960_V1_Device_Online
				 ? "ONLINE" : "STANDBY"));
	  if (OldDeviceState->DeviceState == DAC960_V1_Device_Dead &&
	      NewDeviceState->DeviceState != DAC960_V1_Device_Dead)
	    {
	      Controller->V1.NeedDeviceInquiryInformation = true;
	      Controller->V1.NeedDeviceSerialNumberInformation = true;
	      Controller->V1.DeviceResetCount
			     [Controller->V1.DeviceStateChannel]
			     [Controller->V1.DeviceStateTargetID] = 0;
	    }
	  memcpy(OldDeviceState, NewDeviceState,
		 sizeof(DAC960_V1_DeviceState_T));
	}
      else if (CommandOpcode == DAC960_V1_GetLogicalDriveInformation)
	{
	  int LogicalDriveNumber;
	  for (LogicalDriveNumber = 0;
	       LogicalDriveNumber < Controller->LogicalDriveCount;
	       LogicalDriveNumber++)
	    {
	      DAC960_V1_LogicalDriveInformation_T *OldLogicalDriveInformation =
		&Controller->V1.LogicalDriveInformation[LogicalDriveNumber];
	      DAC960_V1_LogicalDriveInformation_T *NewLogicalDriveInformation =
		&(*Controller->V1.NewLogicalDriveInformation)[LogicalDriveNumber];
	      if (NewLogicalDriveInformation->LogicalDriveState !=
		  OldLogicalDriveInformation->LogicalDriveState)
		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
				"is now %s\n", Controller,
				LogicalDriveNumber,
				Controller->ControllerNumber,
				LogicalDriveNumber,
				(NewLogicalDriveInformation->LogicalDriveState
				 == DAC960_V1_LogicalDrive_Online
				 ? "ONLINE"
				 : NewLogicalDriveInformation->LogicalDriveState
				   == DAC960_V1_LogicalDrive_Critical
				   ? "CRITICAL" : "OFFLINE"));
	      if (NewLogicalDriveInformation->WriteBack !=
		  OldLogicalDriveInformation->WriteBack)
		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
				"is now %s\n", Controller,
				LogicalDriveNumber,
				Controller->ControllerNumber,
				LogicalDriveNumber,
				(NewLogicalDriveInformation->WriteBack
				 ? "WRITE BACK" : "WRITE THRU"));
	    }
	  memcpy(&Controller->V1.LogicalDriveInformation,
		 Controller->V1.NewLogicalDriveInformation,
		 sizeof(DAC960_V1_LogicalDriveInformationArray_T));
	}
      else if (CommandOpcode == DAC960_V1_GetRebuildProgress)
	{
	  unsigned int LogicalDriveNumber =
	    Controller->V1.RebuildProgress->LogicalDriveNumber;
	  unsigned int LogicalDriveSize =
	    Controller->V1.RebuildProgress->LogicalDriveSize;
	  unsigned int BlocksCompleted =
	    LogicalDriveSize - Controller->V1.RebuildProgress->RemainingBlocks;
	  if (CommandStatus == DAC960_V1_NoRebuildOrCheckInProgress &&
	      Controller->V1.LastRebuildStatus == DAC960_V1_NormalCompletion)
	    CommandStatus = DAC960_V1_RebuildSuccessful;
	  switch (CommandStatus)
	    {
	    case DAC960_V1_NormalCompletion:
	      Controller->EphemeralProgressMessage = true;
	      DAC960_Progress("Rebuild in Progress: "
			      "Logical Drive %d (/dev/rd/c%dd%d) "
			      "%d%% completed\n",
			      Controller, LogicalDriveNumber,
			      Controller->ControllerNumber,
			      LogicalDriveNumber,
			      (100 * (BlocksCompleted >> 7))
			      / (LogicalDriveSize >> 7));
	      Controller->EphemeralProgressMessage = false;
	      break;
	    case DAC960_V1_RebuildFailed_LogicalDriveFailure:
	      DAC960_Progress("Rebuild Failed due to "
			      "Logical Drive Failure\n", Controller);
	      break;
	    case DAC960_V1_RebuildFailed_BadBlocksOnOther:
	      DAC960_Progress("Rebuild Failed due to "
			      "Bad Blocks on Other Drives\n", Controller);
	      break;
	    case DAC960_V1_RebuildFailed_NewDriveFailed:
	      DAC960_Progress("Rebuild Failed due to "
			      "Failure of Drive Being Rebuilt\n", Controller);
	      break;
	    case DAC960_V1_NoRebuildOrCheckInProgress:
	      break;
	    case DAC960_V1_RebuildSuccessful:
	      DAC960_Progress("Rebuild Completed Successfully\n", Controller);
	      break;
	    case DAC960_V1_RebuildSuccessfullyTerminated:
	      DAC960_Progress("Rebuild Successfully Terminated\n", Controller);
	      break;
	    }
	  Controller->V1.LastRebuildStatus = CommandStatus;
	  if (CommandType != DAC960_MonitoringCommand &&
	      Controller->V1.RebuildStatusPending)
	    {
	      Command->V1.CommandStatus = Controller->V1.PendingRebuildStatus;
	      Controller->V1.RebuildStatusPending = false;
	    }
	  else if (CommandType == DAC960_MonitoringCommand &&
		   CommandStatus != DAC960_V1_NormalCompletion &&
		   CommandStatus != DAC960_V1_NoRebuildOrCheckInProgress)
	    {
	      Controller->V1.PendingRebuildStatus = CommandStatus;
	      Controller->V1.RebuildStatusPending = true;
	    }
	}
      else if (CommandOpcode == DAC960_V1_RebuildStat)
	{
	  unsigned int LogicalDriveNumber =
	    Controller->V1.RebuildProgress->LogicalDriveNumber;
	  unsigned int LogicalDriveSize =
	    Controller->V1.RebuildProgress->LogicalDriveSize;
	  unsigned int BlocksCompleted =
	    LogicalDriveSize - Controller->V1.RebuildProgress->RemainingBlocks;
	  if (CommandStatus == DAC960_V1_NormalCompletion)
	    {
	      Controller->EphemeralProgressMessage = true;
	      DAC960_Progress("Consistency Check in Progress: "
			      "Logical Drive %d (/dev/rd/c%dd%d) "
			      "%d%% completed\n",
			      Controller, LogicalDriveNumber,
			      Controller->ControllerNumber,
			      LogicalDriveNumber,
			      (100 * (BlocksCompleted >> 7))
			      / (LogicalDriveSize >> 7));
	      Controller->EphemeralProgressMessage = false;
	    }
	}
      else if (CommandOpcode == DAC960_V1_BackgroundInitializationControl)
	{
	  unsigned int LogicalDriveNumber =
	    Controller->V1.BackgroundInitializationStatus->LogicalDriveNumber;
	  unsigned int LogicalDriveSize =
	    Controller->V1.BackgroundInitializationStatus->LogicalDriveSize;
	  unsigned int BlocksCompleted =
	    Controller->V1.BackgroundInitializationStatus->BlocksCompleted;
	  switch (CommandStatus)
	    {
	    case DAC960_V1_NormalCompletion:
	      switch (Controller->V1.BackgroundInitializationStatus->Status)
		{
		case DAC960_V1_BackgroundInitializationInvalid:
		  break;
		case DAC960_V1_BackgroundInitializationStarted:
		  DAC960_Progress("Background Initialization Started\n",
				  Controller);
		  break;
		case DAC960_V1_BackgroundInitializationInProgress:
		  if (BlocksCompleted ==
		      Controller->V1.LastBackgroundInitializationStatus.
				BlocksCompleted &&
		      LogicalDriveNumber ==
		      Controller->V1.LastBackgroundInitializationStatus.
				LogicalDriveNumber)
		    break;
		  Controller->EphemeralProgressMessage = true;
		  DAC960_Progress("Background Initialization in Progress: "
				  "Logical Drive %d (/dev/rd/c%dd%d) "
				  "%d%% completed\n",
				  Controller, LogicalDriveNumber,
				  Controller->ControllerNumber,
				  LogicalDriveNumber,
				  (100 * (BlocksCompleted >> 7))
				  / (LogicalDriveSize >> 7));
		  Controller->EphemeralProgressMessage = false;
		  break;
		case DAC960_V1_BackgroundInitializationSuspended:
		  DAC960_Progress("Background Initialization Suspended\n",
				  Controller);
		  break;
		case DAC960_V1_BackgroundInitializationCancelled:
		  DAC960_Progress("Background Initialization Cancelled\n",
				  Controller);
		  break;
		}
	      memcpy(&Controller->V1.LastBackgroundInitializationStatus,
		     Controller->V1.BackgroundInitializationStatus,
		     sizeof(DAC960_V1_BackgroundInitializationStatus_T));
	      break;
	    case DAC960_V1_BackgroundInitSuccessful:
	      if (Controller->V1.BackgroundInitializationStatus->Status ==
		  DAC960_V1_BackgroundInitializationInProgress)
		DAC960_Progress("Background Initialization "
				"Completed Successfully\n", Controller);
	      Controller->V1.BackgroundInitializationStatus->Status =
		DAC960_V1_BackgroundInitializationInvalid;
	      break;
	    case DAC960_V1_BackgroundInitAborted:
	      if (Controller->V1.BackgroundInitializationStatus->Status ==
		  DAC960_V1_BackgroundInitializationInProgress)
		DAC960_Progress("Background Initialization Aborted\n",
				Controller);
	      Controller->V1.BackgroundInitializationStatus->Status =
		DAC960_V1_BackgroundInitializationInvalid;
	      break;
	    case DAC960_V1_NoBackgroundInitInProgress:
	      break;
	    }
	} 
      else if (CommandOpcode == DAC960_V1_DCDB)
	{
	   /*
	     This is a bit ugly.

	     The InquiryStandardData and 
	     the InquiryUntitSerialNumber information
	     retrieval operations BOTH use the DAC960_V1_DCDB
	     commands.  the test above can't distinguish between
	     these two cases.

	     Instead, we rely on the order of code later in this
             function to ensure that DeviceInquiryInformation commands
             are submitted before DeviceSerialNumber commands.
	   */
	   if (Controller->V1.NeedDeviceInquiryInformation)
	     {
	        DAC960_SCSI_Inquiry_T *InquiryStandardData =
			&Controller->V1.InquiryStandardData
				[Controller->V1.DeviceStateChannel]
				[Controller->V1.DeviceStateTargetID];
	        if (CommandStatus != DAC960_V1_NormalCompletion)
		   {
			memset(InquiryStandardData, 0,
				sizeof(DAC960_SCSI_Inquiry_T));
	      		InquiryStandardData->PeripheralDeviceType = 0x1F;
		    }
	         else
			memcpy(InquiryStandardData, 
				Controller->V1.NewInquiryStandardData,
				sizeof(DAC960_SCSI_Inquiry_T));
	         Controller->V1.NeedDeviceInquiryInformation = false;
              }
	   else if (Controller->V1.NeedDeviceSerialNumberInformation) 
              {
	        DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
		  &Controller->V1.InquiryUnitSerialNumber
				[Controller->V1.DeviceStateChannel]
				[Controller->V1.DeviceStateTargetID];
	         if (CommandStatus != DAC960_V1_NormalCompletion)
		   {
			memset(InquiryUnitSerialNumber, 0,
				sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
	      		InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
		    }
	          else
			memcpy(InquiryUnitSerialNumber, 
				Controller->V1.NewInquiryUnitSerialNumber,
				sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
	      Controller->V1.NeedDeviceSerialNumberInformation = false;
	     }
	}
      /*
        Begin submitting new monitoring commands.
       */
      if (Controller->V1.NewEventLogSequenceNumber
	  - Controller->V1.OldEventLogSequenceNumber > 0)
	{
	  Command->V1.CommandMailbox.Type3E.CommandOpcode =
	    DAC960_V1_PerformEventLogOperation;
	  Command->V1.CommandMailbox.Type3E.OperationType =
	    DAC960_V1_GetEventLogEntry;
	  Command->V1.CommandMailbox.Type3E.OperationQualifier = 1;
	  Command->V1.CommandMailbox.Type3E.SequenceNumber =
	    Controller->V1.OldEventLogSequenceNumber;
	  Command->V1.CommandMailbox.Type3E.BusAddress =
	    	Controller->V1.EventLogEntryDMA;
	  DAC960_QueueCommand(Command);
	  return;
	}
      if (Controller->V1.NeedErrorTableInformation)
	{
	  Controller->V1.NeedErrorTableInformation = false;
	  Command->V1.CommandMailbox.Type3.CommandOpcode =
	    DAC960_V1_GetErrorTable;
	  Command->V1.CommandMailbox.Type3.BusAddress =
	    	Controller->V1.NewErrorTableDMA;
	  DAC960_QueueCommand(Command);
	  return;
	}
      if (Controller->V1.NeedRebuildProgress &&
	  Controller->V1.RebuildProgressFirst)
	{
	  Controller->V1.NeedRebuildProgress = false;
	  Command->V1.CommandMailbox.Type3.CommandOpcode =
	    DAC960_V1_GetRebuildProgress;
	  Command->V1.CommandMailbox.Type3.BusAddress =
	    Controller->V1.RebuildProgressDMA;
	  DAC960_QueueCommand(Command);
	  return;
	}
      if (Controller->V1.NeedDeviceStateInformation)
	{
	  if (Controller->V1.NeedDeviceInquiryInformation)
	    {
	      DAC960_V1_DCDB_T *DCDB = Controller->V1.MonitoringDCDB;
	      dma_addr_t DCDB_DMA = Controller->V1.MonitoringDCDB_DMA;

	      dma_addr_t NewInquiryStandardDataDMA =
		Controller->V1.NewInquiryStandardDataDMA;

	      Command->V1.CommandMailbox.Type3.CommandOpcode = DAC960_V1_DCDB;
	      Command->V1.CommandMailbox.Type3.BusAddress = DCDB_DMA;
	      DCDB->Channel = Controller->V1.DeviceStateChannel;
	      DCDB->TargetID = Controller->V1.DeviceStateTargetID;
	      DCDB->Direction = DAC960_V1_DCDB_DataTransferDeviceToSystem;
	      DCDB->EarlyStatus = false;
	      DCDB->Timeout = DAC960_V1_DCDB_Timeout_10_seconds;
	      DCDB->NoAutomaticRequestSense = false;
	      DCDB->DisconnectPermitted = true;
	      DCDB->TransferLength = sizeof(DAC960_SCSI_Inquiry_T);
	      DCDB->BusAddress = NewInquiryStandardDataDMA;
	      DCDB->CDBLength = 6;
	      DCDB->TransferLengthHigh4 = 0;
	      DCDB->SenseLength = sizeof(DCDB->SenseData);
	      DCDB->CDB[0] = 0x12; /* INQUIRY */
	      DCDB->CDB[1] = 0; /* EVPD = 0 */
	      DCDB->CDB[2] = 0; /* Page Code */
	      DCDB->CDB[3] = 0; /* Reserved */
	      DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_T);
	      DCDB->CDB[5] = 0; /* Control */
	      DAC960_QueueCommand(Command);
	      return;
	    }
	  if (Controller->V1.NeedDeviceSerialNumberInformation)
	    {
	      DAC960_V1_DCDB_T *DCDB = Controller->V1.MonitoringDCDB;
	      dma_addr_t DCDB_DMA = Controller->V1.MonitoringDCDB_DMA;
	      dma_addr_t NewInquiryUnitSerialNumberDMA = 
			Controller->V1.NewInquiryUnitSerialNumberDMA;

	      Command->V1.CommandMailbox.Type3.CommandOpcode = DAC960_V1_DCDB;
	      Command->V1.CommandMailbox.Type3.BusAddress = DCDB_DMA;
	      DCDB->Channel = Controller->V1.DeviceStateChannel;
	      DCDB->TargetID = Controller->V1.DeviceStateTargetID;
	      DCDB->Direction = DAC960_V1_DCDB_DataTransferDeviceToSystem;
	      DCDB->EarlyStatus = false;
	      DCDB->Timeout = DAC960_V1_DCDB_Timeout_10_seconds;
	      DCDB->NoAutomaticRequestSense = false;
	      DCDB->DisconnectPermitted = true;
	      DCDB->TransferLength =
		sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
	      DCDB->BusAddress = NewInquiryUnitSerialNumberDMA;
	      DCDB->CDBLength = 6;
	      DCDB->TransferLengthHigh4 = 0;
	      DCDB->SenseLength = sizeof(DCDB->SenseData);
	      DCDB->CDB[0] = 0x12; /* INQUIRY */
	      DCDB->CDB[1] = 1; /* EVPD = 1 */
	      DCDB->CDB[2] = 0x80; /* Page Code */
	      DCDB->CDB[3] = 0; /* Reserved */
	      DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
	      DCDB->CDB[5] = 0; /* Control */
	      DAC960_QueueCommand(Command);
	      return;
	    }
	  if (Controller->V1.StartDeviceStateScan)
	    {
	      Controller->V1.DeviceStateChannel = 0;
	      Controller->V1.DeviceStateTargetID = 0;
	      Controller->V1.StartDeviceStateScan = false;
	    }
	  else if (++Controller->V1.DeviceStateTargetID == Controller->Targets)
	    {
	      Controller->V1.DeviceStateChannel++;
	      Controller->V1.DeviceStateTargetID = 0;
	    }
	  if (Controller->V1.DeviceStateChannel < Controller->Channels)
	    {
	      Controller->V1.NewDeviceState->DeviceState =
		DAC960_V1_Device_Dead;
	      Command->V1.CommandMailbox.Type3D.CommandOpcode =
		DAC960_V1_GetDeviceState;
	      Command->V1.CommandMailbox.Type3D.Channel =
		Controller->V1.DeviceStateChannel;
	      Command->V1.CommandMailbox.Type3D.TargetID =
		Controller->V1.DeviceStateTargetID;
	      Command->V1.CommandMailbox.Type3D.BusAddress =
		Controller->V1.NewDeviceStateDMA;
	      DAC960_QueueCommand(Command);
	      return;
	    }
	  Controller->V1.NeedDeviceStateInformation = false;
	}
      if (Controller->V1.NeedLogicalDriveInformation)
	{
	  Controller->V1.NeedLogicalDriveInformation = false;
	  Command->V1.CommandMailbox.Type3.CommandOpcode =
	    DAC960_V1_GetLogicalDriveInformation;
	  Command->V1.CommandMailbox.Type3.BusAddress =
	    Controller->V1.NewLogicalDriveInformationDMA;
	  DAC960_QueueCommand(Command);
	  return;
	}
      if (Controller->V1.NeedRebuildProgress)
	{
	  Controller->V1.NeedRebuildProgress = false;
	  Command->V1.CommandMailbox.Type3.CommandOpcode =
	    DAC960_V1_GetRebuildProgress;
	  Command->V1.CommandMailbox.Type3.BusAddress =
	    	Controller->V1.RebuildProgressDMA;
	  DAC960_QueueCommand(Command);
	  return;
	}
      if (Controller->V1.NeedConsistencyCheckProgress)
	{
	  Controller->V1.NeedConsistencyCheckProgress = false;
	  Command->V1.CommandMailbox.Type3.CommandOpcode =
	    DAC960_V1_RebuildStat;
	  Command->V1.CommandMailbox.Type3.BusAddress =
	    Controller->V1.RebuildProgressDMA;
	  DAC960_QueueCommand(Command);
	  return;
	}
      if (Controller->V1.NeedBackgroundInitializationStatus)
	{
	  Controller->V1.NeedBackgroundInitializationStatus = false;
	  Command->V1.CommandMailbox.Type3B.CommandOpcode =
	    DAC960_V1_BackgroundInitializationControl;
	  Command->V1.CommandMailbox.Type3B.CommandOpcode2 = 0x20;
	  Command->V1.CommandMailbox.Type3B.BusAddress =
	    Controller->V1.BackgroundInitializationStatusDMA;
	  DAC960_QueueCommand(Command);
	  return;
	}
      Controller->MonitoringTimerCount++;
      Controller->MonitoringTimer.expires =
	jiffies + DAC960_MonitoringTimerInterval;
      	add_timer(&Controller->MonitoringTimer);
    }
  if (CommandType == DAC960_ImmediateCommand)
    {
      complete(Command->Completion);
      Command->Completion = NULL;
      return;
    }
  if (CommandType == DAC960_QueuedCommand)
    {
      DAC960_V1_KernelCommand_T *KernelCommand = Command->V1.KernelCommand;
      KernelCommand->CommandStatus = Command->V1.CommandStatus;
      Command->V1.KernelCommand = NULL;
      if (CommandOpcode == DAC960_V1_DCDB)
	Controller->V1.DirectCommandActive[KernelCommand->DCDB->Channel]
					  [KernelCommand->DCDB->TargetID] =
	  false;
      DAC960_DeallocateCommand(Command);
      KernelCommand->CompletionFunction(KernelCommand);
      return;
    }
  /*
    Queue a Status Monitoring Command to the Controller using the just
    completed Command if one was deferred previously due to lack of a
    free Command when the Monitoring Timer Function was called.
  */
  if (Controller->MonitoringCommandDeferred)
    {
      Controller->MonitoringCommandDeferred = false;
      DAC960_V1_QueueMonitoringCommand(Command);
      return;
    }
  /*
    Deallocate the Command.
  */
  DAC960_DeallocateCommand(Command);
  /*
    Wake up any processes waiting on a free Command.
  */
  wake_up(&Controller->CommandWaitQueue);
}


/*
  DAC960_V2_ReadWriteError prints an appropriate error message for Command
  when an error occurs on a Read or Write operation.
*/

static void DAC960_V2_ReadWriteError(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  unsigned char *SenseErrors[] = { "NO SENSE", "RECOVERED ERROR",
				   "NOT READY", "MEDIUM ERROR",
				   "HARDWARE ERROR", "ILLEGAL REQUEST",
				   "UNIT ATTENTION", "DATA PROTECT",
				   "BLANK CHECK", "VENDOR-SPECIFIC",
				   "COPY ABORTED", "ABORTED COMMAND",
				   "EQUAL", "VOLUME OVERFLOW",
				   "MISCOMPARE", "RESERVED" };
  unsigned char *CommandName = "UNKNOWN";
  switch (Command->CommandType)
    {
    case DAC960_ReadCommand:
    case DAC960_ReadRetryCommand:
      CommandName = "READ";
      break;
    case DAC960_WriteCommand:
    case DAC960_WriteRetryCommand:
      CommandName = "WRITE";
      break;
    case DAC960_MonitoringCommand:
    case DAC960_ImmediateCommand:
    case DAC960_QueuedCommand:
      break;
    }
  DAC960_Error("Error Condition %s on %s:\n", Controller,
	       SenseErrors[Command->V2.RequestSense->SenseKey], CommandName);
  DAC960_Error("  /dev/rd/c%dd%d:   absolute blocks %u..%u\n",
	       Controller, Controller->ControllerNumber,
	       Command->LogicalDriveNumber, Command->BlockNumber,
	       Command->BlockNumber + Command->BlockCount - 1);
}


/*
  DAC960_V2_ReportEvent prints an appropriate message when a Controller Event
  occurs.
*/

static void DAC960_V2_ReportEvent(DAC960_Controller_T *Controller,
				  DAC960_V2_Event_T *Event)
{
  DAC960_SCSI_RequestSense_T *RequestSense =
    (DAC960_SCSI_RequestSense_T *) &Event->RequestSenseData;
  unsigned char MessageBuffer[DAC960_LineBufferSize];
  static struct { int EventCode; unsigned char *EventMessage; } EventList[] =
    { /* Physical Device Events (0x0000 - 0x007F) */
      { 0x0001, "P Online" },
      { 0x0002, "P Standby" },
      { 0x0005, "P Automatic Rebuild Started" },
      { 0x0006, "P Manual Rebuild Started" },
      { 0x0007, "P Rebuild Completed" },
      { 0x0008, "P Rebuild Cancelled" },
      { 0x0009, "P Rebuild Failed for Unknown Reasons" },
      { 0x000A, "P Rebuild Failed due to New Physical Device" },
      { 0x000B, "P Rebuild Failed due to Logical Drive Failure" },
      { 0x000C, "S Offline" },
      { 0x000D, "P Found" },
      { 0x000E, "P Removed" },
      { 0x000F, "P Unconfigured" },
      { 0x0010, "P Expand Capacity Started" },
      { 0x0011, "P Expand Capacity Completed" },
      { 0x0012, "P Expand Capacity Failed" },
      { 0x0013, "P Command Timed Out" },
      { 0x0014, "P Command Aborted" },
      { 0x0015, "P Command Retried" },
      { 0x0016, "P Parity Error" },
      { 0x0017, "P Soft Error" },
      { 0x0018, "P Miscellaneous Error" },
      { 0x0019, "P Reset" },
      { 0x001A, "P Active Spare Found" },
      { 0x001B, "P Warm Spare Found" },
      { 0x001C, "S Sense Data Received" },
      { 0x001D, "P Initialization Started" },
      { 0x001E, "P Initialization Completed" },
      { 0x001F, "P Initialization Failed" },
      { 0x0020, "P Initialization Cancelled" },
      { 0x0021, "P Failed because Write Recovery Failed" },
      { 0x0022, "P Failed because SCSI Bus Reset Failed" },
      { 0x0023, "P Failed because of Double Check Condition" },
      { 0x0024, "P Failed because Device Cannot Be Accessed" },
      { 0x0025, "P Failed because of Gross Error on SCSI Processor" },
      { 0x0026, "P Failed because of Bad Tag from Device" },
      { 0x0027, "P Failed because of Command Timeout" },
      { 0x0028, "P Failed because of System Reset" },
      { 0x0029, "P Failed because of Busy Status or Parity Error" },
      { 0x002A, "P Failed because Host Set Device to Failed State" },
      { 0x002B, "P Failed because of Selection Timeout" },
      { 0x002C, "P Failed because of SCSI Bus Phase Error" },
      { 0x002D, "P Failed because Device Returned Unknown Status" },
      { 0x002E, "P Failed because Device Not Ready" },
      { 0x002F, "P Failed because Device Not Found at Startup" },
      { 0x0030, "P Failed because COD Write Operation Failed" },
      { 0x0031, "P Failed because BDT Write Operation Failed" },
      { 0x0039, "P Missing at Startup" },
      { 0x003A, "P Start Rebuild Failed due to Physical Drive Too Small" },
      { 0x003C, "P Temporarily Offline Device Automatically Made Online" },
      { 0x003D, "P Standby Rebuild Started" },
      /* Logical Device Events (0x0080 - 0x00FF) */
      { 0x0080, "M Consistency Check Started" },
      { 0x0081, "M Consistency Check Completed" },
      { 0x0082, "M Consistency Check Cancelled" },
      { 0x0083, "M Consistency Check Completed With Errors" },
      { 0x0084, "M Consistency Check Failed due to Logical Drive Failure" },
      { 0x0085, "M Consistency Check Failed due to Physical Device Failure" },
      { 0x0086, "L Offline" },
      { 0x0087, "L Critical" },
      { 0x0088, "L Online" },
      { 0x0089, "M Automatic Rebuild Started" },
      { 0x008A, "M Manual Rebuild Started" },
      { 0x008B, "M Rebuild Completed" },
      { 0x008C, "M Rebuild Cancelled" },
      { 0x008D, "M Rebuild Failed for Unknown Reasons" },
      { 0x008E, "M Rebuild Failed due to New Physical Device" },
      { 0x008F, "M Rebuild Failed due to Logical Drive Failure" },
      { 0x0090, "M Initialization Started" },
      { 0x0091, "M Initialization Completed" },
      { 0x0092, "M Initialization Cancelled" },
      { 0x0093, "M Initialization Failed" },
      { 0x0094, "L Found" },
      { 0x0095, "L Deleted" },
      { 0x0096, "M Expand Capacity Started" },
      { 0x0097, "M Expand Capacity Completed" },
      { 0x0098, "M Expand Capacity Failed" },
      { 0x0099, "L Bad Block Found" },
      { 0x009A, "L Size Changed" },
      { 0x009B, "L Type Changed" },
      { 0x009C, "L Bad Data Block Found" },
      { 0x009E, "L Read of Data Block in BDT" },
      { 0x009F, "L Write Back Data for Disk Block Lost" },
      { 0x00A0, "L Temporarily Offline RAID-5/3 Drive Made Online" },
      { 0x00A1, "L Temporarily Offline RAID-6/1/0/7 Drive Made Online" },
      { 0x00A2, "L Standby Rebuild Started" },
      /* Fault Management Events (0x0100 - 0x017F) */
      { 0x0140, "E Fan %d Failed" },
      { 0x0141, "E Fan %d OK" },
      { 0x0142, "E Fan %d Not Present" },
      { 0x0143, "E Power Supply %d Failed" },
      { 0x0144, "E Power Supply %d OK" },
      { 0x0145, "E Power Supply %d Not Present" },
      { 0x0146, "E Temperature Sensor %d Temperature Exceeds Safe Limit" },
      { 0x0147, "E Temperature Sensor %d Temperature Exceeds Working Limit" },
      { 0x0148, "E Temperature Sensor %d Temperature Normal" },
      { 0x0149, "E Temperature Sensor %d Not Present" },
      { 0x014A, "E Enclosure Management Unit %d Access Critical" },
      { 0x014B, "E Enclosure Management Unit %d Access OK" },
      { 0x014C, "E Enclosure Management Unit %d Access Offline" },
      /* Controller Events (0x0180 - 0x01FF) */
      { 0x0181, "C Cache Write Back Error" },
      { 0x0188, "C Battery Backup Unit Found" },
      { 0x0189, "C Battery Backup Unit Charge Level Low" },
      { 0x018A, "C Battery Backup Unit Charge Level OK" },
      { 0x0193, "C Installation Aborted" },
      { 0x0195, "C Battery Backup Unit Physically Removed" },
      { 0x0196, "C Memory Error During Warm Boot" },
      { 0x019E, "C Memory Soft ECC Error Corrected" },
      { 0x019F, "C Memory Hard ECC Error Corrected" },
      { 0x01A2, "C Battery Backup Unit Failed" },
      { 0x01AB, "C Mirror Race Recovery Failed" },
      { 0x01AC, "C Mirror Race on Critical Drive" },
      /* Controller Internal Processor Events */
      { 0x0380, "C Internal Controller Hung" },
      { 0x0381, "C Internal Controller Firmware Breakpoint" },
      { 0x0390, "C Internal Controller i960 Processor Specific Error" },
      { 0x03A0, "C Internal Controller StrongARM Processor Specific Error" },
      { 0, "" } };
  int EventListIndex = 0, EventCode;
  unsigned char EventType, *EventMessage;
  if (Event->EventCode == 0x1C &&
      RequestSense->SenseKey == DAC960_SenseKey_VendorSpecific &&
      (RequestSense->AdditionalSenseCode == 0x80 ||
       RequestSense->AdditionalSenseCode == 0x81))
    Event->EventCode = ((RequestSense->AdditionalSenseCode - 0x80) << 8) |
		       RequestSense->AdditionalSenseCodeQualifier;
  while (true)
    {
      EventCode = EventList[EventListIndex].EventCode;
      if (EventCode == Event->EventCode || EventCode == 0) break;
      EventListIndex++;
    }
  EventType = EventList[EventListIndex].EventMessage[0];
  EventMessage = &EventList[EventListIndex].EventMessage[2];
  if (EventCode == 0)
    {
      DAC960_Critical("Unknown Controller Event Code %04X\n",
		      Controller, Event->EventCode);
      return;
    }
  switch (EventType)
    {
    case 'P':
      DAC960_Critical("Physical Device %d:%d %s\n", Controller,
		      Event->Channel, Event->TargetID, EventMessage);
      break;
    case 'L':
      DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) %s\n", Controller,
		      Event->LogicalUnit, Controller->ControllerNumber,
		      Event->LogicalUnit, EventMessage);
      break;
    case 'M':
      DAC960_Progress("Logical Drive %d (/dev/rd/c%dd%d) %s\n", Controller,
		      Event->LogicalUnit, Controller->ControllerNumber,
		      Event->LogicalUnit, EventMessage);
      break;
    case 'S':
      if (RequestSense->SenseKey == DAC960_SenseKey_NoSense ||
	  (RequestSense->SenseKey == DAC960_SenseKey_NotReady &&
	   RequestSense->AdditionalSenseCode == 0x04 &&
	   (RequestSense->AdditionalSenseCodeQualifier == 0x01 ||
	    RequestSense->AdditionalSenseCodeQualifier == 0x02)))
	break;
      DAC960_Critical("Physical Device %d:%d %s\n", Controller,
		      Event->Channel, Event->TargetID, EventMessage);
      DAC960_Critical("Physical Device %d:%d Request Sense: "
		      "Sense Key = %X, ASC = %02X, ASCQ = %02X\n",
		      Controller,
		      Event->Channel,
		      Event->TargetID,
		      RequestSense->SenseKey,
		      RequestSense->AdditionalSenseCode,
		      RequestSense->AdditionalSenseCodeQualifier);
      DAC960_Critical("Physical Device %d:%d Request Sense: "
		      "Information = %02X%02X%02X%02X "
		      "%02X%02X%02X%02X\n",
		      Controller,
		      Event->Channel,
		      Event->TargetID,
		      RequestSense->Information[0],
		      RequestSense->Information[1],
		      RequestSense->Information[2],
		      RequestSense->Information[3],
		      RequestSense->CommandSpecificInformation[0],
		      RequestSense->CommandSpecificInformation[1],
		      RequestSense->CommandSpecificInformation[2],
		      RequestSense->CommandSpecificInformation[3]);
      break;
    case 'E':
      if (Controller->SuppressEnclosureMessages) break;
      sprintf(MessageBuffer, EventMessage, Event->LogicalUnit);
      DAC960_Critical("Enclosure %d %s\n", Controller,
		      Event->TargetID, MessageBuffer);
      break;
    case 'C':
      DAC960_Critical("Controller %s\n", Controller, EventMessage);
      break;
    default:
      DAC960_Critical("Unknown Controller Event Code %04X\n",
		      Controller, Event->EventCode);
      break;
    }
}


/*
  DAC960_V2_ReportProgress prints an appropriate progress message for
  Logical Device Long Operations.
*/

static void DAC960_V2_ReportProgress(DAC960_Controller_T *Controller,
				     unsigned char *MessageString,
				     unsigned int LogicalDeviceNumber,
				     unsigned long BlocksCompleted,
				     unsigned long LogicalDeviceSize)
{
  Controller->EphemeralProgressMessage = true;
  DAC960_Progress("%s in Progress: Logical Drive %d (/dev/rd/c%dd%d) "
		  "%d%% completed\n", Controller,
		  MessageString,
		  LogicalDeviceNumber,
		  Controller->ControllerNumber,
		  LogicalDeviceNumber,
		  (100 * (BlocksCompleted >> 7)) / (LogicalDeviceSize >> 7));
  Controller->EphemeralProgressMessage = false;
}


/*
  DAC960_V2_ProcessCompletedCommand performs completion processing for Command
  for DAC960 V2 Firmware Controllers.
*/

static void DAC960_V2_ProcessCompletedCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  DAC960_CommandType_T CommandType = Command->CommandType;
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  DAC960_V2_IOCTL_Opcode_T CommandOpcode = CommandMailbox->Common.IOCTL_Opcode;
  DAC960_V2_CommandStatus_T CommandStatus = Command->V2.CommandStatus;

  if (CommandType == DAC960_ReadCommand ||
      CommandType == DAC960_WriteCommand)
    {

#ifdef FORCE_RETRY_DEBUG
      CommandStatus = DAC960_V2_AbormalCompletion;
#endif
      Command->V2.RequestSense->SenseKey = DAC960_SenseKey_MediumError;

      if (CommandStatus == DAC960_V2_NormalCompletion) {

		if (!DAC960_ProcessCompletedRequest(Command, true))
			BUG();

      } else if (Command->V2.RequestSense->SenseKey == DAC960_SenseKey_MediumError)
	{
	  /*
	   * break the command down into pieces and resubmit each
	   * piece, hoping that some of them will succeed.
	   */
	   DAC960_queue_partial_rw(Command);
	   return;
	}
      else
	{
	  if (Command->V2.RequestSense->SenseKey != DAC960_SenseKey_NotReady)
	    DAC960_V2_ReadWriteError(Command);
	  /*
	    Perform completion processing for all buffers in this I/O Request.
	  */
          (void)DAC960_ProcessCompletedRequest(Command, false);
	}
    }
  else if (CommandType == DAC960_ReadRetryCommand ||
	   CommandType == DAC960_WriteRetryCommand)
    {
      bool normal_completion;

#ifdef FORCE_RETRY_FAILURE_DEBUG
      static int retry_count = 1;
#endif
      /*
        Perform completion processing for the portion that was
	retried, and submit the next portion, if any.
      */
      normal_completion = true;
      if (CommandStatus != DAC960_V2_NormalCompletion) {
	normal_completion = false;
	if (Command->V2.RequestSense->SenseKey != DAC960_SenseKey_NotReady)
	    DAC960_V2_ReadWriteError(Command);
      }

#ifdef FORCE_RETRY_FAILURE_DEBUG
      if (!(++retry_count % 10000)) {
	      printk("V2 error retry failure test\n");
	      normal_completion = false;
	      DAC960_V2_ReadWriteError(Command);
      }
#endif

      if (!DAC960_ProcessCompletedRequest(Command, normal_completion)) {
		DAC960_queue_partial_rw(Command);
        	return;
      }
    }
  else if (CommandType == DAC960_MonitoringCommand)
    {
      if (Controller->ShutdownMonitoringTimer)
	      return;
      if (CommandOpcode == DAC960_V2_GetControllerInfo)
	{
	  DAC960_V2_ControllerInfo_T *NewControllerInfo =
	    Controller->V2.NewControllerInformation;
	  DAC960_V2_ControllerInfo_T *ControllerInfo =
	    &Controller->V2.ControllerInformation;
	  Controller->LogicalDriveCount =
	    NewControllerInfo->LogicalDevicesPresent;
	  Controller->V2.NeedLogicalDeviceInformation = true;
	  Controller->V2.NeedPhysicalDeviceInformation = true;
	  Controller->V2.StartLogicalDeviceInformationScan = true;
	  Controller->V2.StartPhysicalDeviceInformationScan = true;
	  Controller->MonitoringAlertMode =
	    (NewControllerInfo->LogicalDevicesCritical > 0 ||
	     NewControllerInfo->LogicalDevicesOffline > 0 ||
	     NewControllerInfo->PhysicalDisksCritical > 0 ||
	     NewControllerInfo->PhysicalDisksOffline > 0);
	  memcpy(ControllerInfo, NewControllerInfo,
		 sizeof(DAC960_V2_ControllerInfo_T));
	}
      else if (CommandOpcode == DAC960_V2_GetEvent)
	{
	  if (CommandStatus == DAC960_V2_NormalCompletion) {
	    DAC960_V2_ReportEvent(Controller, Controller->V2.Event);
	  }
	  Controller->V2.NextEventSequenceNumber++;
	}
      else if (CommandOpcode == DAC960_V2_GetPhysicalDeviceInfoValid &&
	       CommandStatus == DAC960_V2_NormalCompletion)
	{
	  DAC960_V2_PhysicalDeviceInfo_T *NewPhysicalDeviceInfo =
	    Controller->V2.NewPhysicalDeviceInformation;
	  unsigned int PhysicalDeviceIndex = Controller->V2.PhysicalDeviceIndex;
	  DAC960_V2_PhysicalDeviceInfo_T *PhysicalDeviceInfo =
	    Controller->V2.PhysicalDeviceInformation[PhysicalDeviceIndex];
	  DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
	    Controller->V2.InquiryUnitSerialNumber[PhysicalDeviceIndex];
	  unsigned int DeviceIndex;
	  while (PhysicalDeviceInfo != NULL &&
		 (NewPhysicalDeviceInfo->Channel >
		  PhysicalDeviceInfo->Channel ||
		  (NewPhysicalDeviceInfo->Channel ==
		   PhysicalDeviceInfo->Channel &&
		   (NewPhysicalDeviceInfo->TargetID >
		    PhysicalDeviceInfo->TargetID ||
		   (NewPhysicalDeviceInfo->TargetID ==
		    PhysicalDeviceInfo->TargetID &&
		    NewPhysicalDeviceInfo->LogicalUnit >
		    PhysicalDeviceInfo->LogicalUnit)))))
	    {
	      DAC960_Critical("Physical Device %d:%d No Longer Exists\n",
			      Controller,
			      PhysicalDeviceInfo->Channel,
			      PhysicalDeviceInfo->TargetID);
	      Controller->V2.PhysicalDeviceInformation
			     [PhysicalDeviceIndex] = NULL;
	      Controller->V2.InquiryUnitSerialNumber
			     [PhysicalDeviceIndex] = NULL;
	      kfree(PhysicalDeviceInfo);
	      kfree(InquiryUnitSerialNumber);
	      for (DeviceIndex = PhysicalDeviceIndex;
		   DeviceIndex < DAC960_V2_MaxPhysicalDevices - 1;
		   DeviceIndex++)
		{
		  Controller->V2.PhysicalDeviceInformation[DeviceIndex] =
		    Controller->V2.PhysicalDeviceInformation[DeviceIndex+1];
		  Controller->V2.InquiryUnitSerialNumber[DeviceIndex] =
		    Controller->V2.InquiryUnitSerialNumber[DeviceIndex+1];
		}
	      Controller->V2.PhysicalDeviceInformation
			     [DAC960_V2_MaxPhysicalDevices-1] = NULL;
	      Controller->V2.InquiryUnitSerialNumber
			     [DAC960_V2_MaxPhysicalDevices-1] = NULL;
	      PhysicalDeviceInfo =
		Controller->V2.PhysicalDeviceInformation[PhysicalDeviceIndex];
	      InquiryUnitSerialNumber =
		Controller->V2.InquiryUnitSerialNumber[PhysicalDeviceIndex];
	    }
	  if (PhysicalDeviceInfo == NULL ||
	      (NewPhysicalDeviceInfo->Channel !=
	       PhysicalDeviceInfo->Channel) ||
	      (NewPhysicalDeviceInfo->TargetID !=
	       PhysicalDeviceInfo->TargetID) ||
	      (NewPhysicalDeviceInfo->LogicalUnit !=
	       PhysicalDeviceInfo->LogicalUnit))
	    {
	      PhysicalDeviceInfo =
		kmalloc(sizeof(DAC960_V2_PhysicalDeviceInfo_T), GFP_ATOMIC);
	      InquiryUnitSerialNumber =
		  kmalloc(sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T),
			  GFP_ATOMIC);
	      if (InquiryUnitSerialNumber == NULL ||
		  PhysicalDeviceInfo == NULL)
		{
		  kfree(InquiryUnitSerialNumber);
		  InquiryUnitSerialNumber = NULL;
		  kfree(PhysicalDeviceInfo);
		  PhysicalDeviceInfo = NULL;
		}
	      DAC960_Critical("Physical Device %d:%d Now Exists%s\n",
			      Controller,
			      NewPhysicalDeviceInfo->Channel,
			      NewPhysicalDeviceInfo->TargetID,
			      (PhysicalDeviceInfo != NULL
			       ? "" : " - Allocation Failed"));
	      if (PhysicalDeviceInfo != NULL)
		{
		  memset(PhysicalDeviceInfo, 0,
			 sizeof(DAC960_V2_PhysicalDeviceInfo_T));
		  PhysicalDeviceInfo->PhysicalDeviceState =
		    DAC960_V2_Device_InvalidState;
		  memset(InquiryUnitSerialNumber, 0,
			 sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
		  InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
		  for (DeviceIndex = DAC960_V2_MaxPhysicalDevices - 1;
		       DeviceIndex > PhysicalDeviceIndex;
		       DeviceIndex--)
		    {
		      Controller->V2.PhysicalDeviceInformation[DeviceIndex] =
			Controller->V2.PhysicalDeviceInformation[DeviceIndex-1];
		      Controller->V2.InquiryUnitSerialNumber[DeviceIndex] =
			Controller->V2.InquiryUnitSerialNumber[DeviceIndex-1];
		    }
		  Controller->V2.PhysicalDeviceInformation
				 [PhysicalDeviceIndex] =
		    PhysicalDeviceInfo;
		  Controller->V2.InquiryUnitSerialNumber
				 [PhysicalDeviceIndex] =
		    InquiryUnitSerialNumber;
		  Controller->V2.NeedDeviceSerialNumberInformation = true;
		}
	    }
	  if (PhysicalDeviceInfo != NULL)
	    {
	      if (NewPhysicalDeviceInfo->PhysicalDeviceState !=
		  PhysicalDeviceInfo->PhysicalDeviceState)
		DAC960_Critical(
		  "Physical Device %d:%d is now %s\n", Controller,
		  NewPhysicalDeviceInfo->Channel,
		  NewPhysicalDeviceInfo->TargetID,
		  (NewPhysicalDeviceInfo->PhysicalDeviceState
		   == DAC960_V2_Device_Online
		   ? "ONLINE"
		   : NewPhysicalDeviceInfo->PhysicalDeviceState
		     == DAC960_V2_Device_Rebuild
		     ? "REBUILD"
		     : NewPhysicalDeviceInfo->PhysicalDeviceState
		       == DAC960_V2_Device_Missing
		       ? "MISSING"
		       : NewPhysicalDeviceInfo->PhysicalDeviceState
			 == DAC960_V2_Device_Critical
			 ? "CRITICAL"
			 : NewPhysicalDeviceInfo->PhysicalDeviceState
			   == DAC960_V2_Device_Dead
			   ? "DEAD"
			   : NewPhysicalDeviceInfo->PhysicalDeviceState
			     == DAC960_V2_Device_SuspectedDead
			     ? "SUSPECTED-DEAD"
			     : NewPhysicalDeviceInfo->PhysicalDeviceState
			       == DAC960_V2_Device_CommandedOffline
			       ? "COMMANDED-OFFLINE"
			       : NewPhysicalDeviceInfo->PhysicalDeviceState
				 == DAC960_V2_Device_Standby
				 ? "STANDBY" : "UNKNOWN"));
	      if ((NewPhysicalDeviceInfo->ParityErrors !=
		   PhysicalDeviceInfo->ParityErrors) ||
		  (NewPhysicalDeviceInfo->SoftErrors !=
		   PhysicalDeviceInfo->SoftErrors) ||
		  (NewPhysicalDeviceInfo->HardErrors !=
		   PhysicalDeviceInfo->HardErrors) ||
		  (NewPhysicalDeviceInfo->MiscellaneousErrors !=
		   PhysicalDeviceInfo->MiscellaneousErrors) ||
		  (NewPhysicalDeviceInfo->CommandTimeouts !=
		   PhysicalDeviceInfo->CommandTimeouts) ||
		  (NewPhysicalDeviceInfo->Retries !=
		   PhysicalDeviceInfo->Retries) ||
		  (NewPhysicalDeviceInfo->Aborts !=
		   PhysicalDeviceInfo->Aborts) ||
		  (NewPhysicalDeviceInfo->PredictedFailuresDetected !=
		   PhysicalDeviceInfo->PredictedFailuresDetected))
		{
		  DAC960_Critical("Physical Device %d:%d Errors: "
				  "Parity = %d, Soft = %d, "
				  "Hard = %d, Misc = %d\n",
				  Controller,
				  NewPhysicalDeviceInfo->Channel,
				  NewPhysicalDeviceInfo->TargetID,
				  NewPhysicalDeviceInfo->ParityErrors,
				  NewPhysicalDeviceInfo->SoftErrors,
				  NewPhysicalDeviceInfo->HardErrors,
				  NewPhysicalDeviceInfo->MiscellaneousErrors);
		  DAC960_Critical("Physical Device %d:%d Errors: "
				  "Timeouts = %d, Retries = %d, "
				  "Aborts = %d, Predicted = %d\n",
				  Controller,
				  NewPhysicalDeviceInfo->Channel,
				  NewPhysicalDeviceInfo->TargetID,
				  NewPhysicalDeviceInfo->CommandTimeouts,
				  NewPhysicalDeviceInfo->Retries,
				  NewPhysicalDeviceInfo->Aborts,
				  NewPhysicalDeviceInfo
				  ->PredictedFailuresDetected);
		}
	      if ((PhysicalDeviceInfo->PhysicalDeviceState
		   == DAC960_V2_Device_Dead ||
		   PhysicalDeviceInfo->PhysicalDeviceState
		   == DAC960_V2_Device_InvalidState) &&
		  NewPhysicalDeviceInfo->PhysicalDeviceState
		  != DAC960_V2_Device_Dead)
		Controller->V2.NeedDeviceSerialNumberInformation = true;
	      memcpy(PhysicalDeviceInfo, NewPhysicalDeviceInfo,
		     sizeof(DAC960_V2_PhysicalDeviceInfo_T));
	    }
	  NewPhysicalDeviceInfo->LogicalUnit++;
	  Controller->V2.PhysicalDeviceIndex++;
	}
      else if (CommandOpcode == DAC960_V2_GetPhysicalDeviceInfoValid)
	{
	  unsigned int DeviceIndex;
	  for (DeviceIndex = Controller->V2.PhysicalDeviceIndex;
	       DeviceIndex < DAC960_V2_MaxPhysicalDevices;
	       DeviceIndex++)
	    {
	      DAC960_V2_PhysicalDeviceInfo_T *PhysicalDeviceInfo =
		Controller->V2.PhysicalDeviceInformation[DeviceIndex];
	      DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
		Controller->V2.InquiryUnitSerialNumber[DeviceIndex];
	      if (PhysicalDeviceInfo == NULL) break;
	      DAC960_Critical("Physical Device %d:%d No Longer Exists\n",
			      Controller,
			      PhysicalDeviceInfo->Channel,
			      PhysicalDeviceInfo->TargetID);
	      Controller->V2.PhysicalDeviceInformation[DeviceIndex] = NULL;
	      Controller->V2.InquiryUnitSerialNumber[DeviceIndex] = NULL;
	      kfree(PhysicalDeviceInfo);
	      kfree(InquiryUnitSerialNumber);
	    }
	  Controller->V2.NeedPhysicalDeviceInformation = false;
	}
      else if (CommandOpcode == DAC960_V2_GetLogicalDeviceInfoValid &&
	       CommandStatus == DAC960_V2_NormalCompletion)
	{
	  DAC960_V2_LogicalDeviceInfo_T *NewLogicalDeviceInfo =
	    Controller->V2.NewLogicalDeviceInformation;
	  unsigned short LogicalDeviceNumber =
	    NewLogicalDeviceInfo->LogicalDeviceNumber;
	  DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
	    Controller->V2.LogicalDeviceInformation[LogicalDeviceNumber];
	  if (LogicalDeviceInfo == NULL)
	    {
	      DAC960_V2_PhysicalDevice_T PhysicalDevice;
	      PhysicalDevice.Controller = 0;
	      PhysicalDevice.Channel = NewLogicalDeviceInfo->Channel;
	      PhysicalDevice.TargetID = NewLogicalDeviceInfo->TargetID;
	      PhysicalDevice.LogicalUnit = NewLogicalDeviceInfo->LogicalUnit;
	      Controller->V2.LogicalDriveToVirtualDevice[LogicalDeviceNumber] =
		PhysicalDevice;
	      LogicalDeviceInfo = kmalloc(sizeof(DAC960_V2_LogicalDeviceInfo_T),
					  GFP_ATOMIC);
	      Controller->V2.LogicalDeviceInformation[LogicalDeviceNumber] =
		LogicalDeviceInfo;
	      DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
			      "Now Exists%s\n", Controller,
			      LogicalDeviceNumber,
			      Controller->ControllerNumber,
			      LogicalDeviceNumber,
			      (LogicalDeviceInfo != NULL
			       ? "" : " - Allocation Failed"));
	      if (LogicalDeviceInfo != NULL)
		{
		  memset(LogicalDeviceInfo, 0,
			 sizeof(DAC960_V2_LogicalDeviceInfo_T));
		  DAC960_ComputeGenericDiskInfo(Controller);
		}
	    }
	  if (LogicalDeviceInfo != NULL)
	    {
	      unsigned long LogicalDeviceSize =
		NewLogicalDeviceInfo->ConfigurableDeviceSize;
	      if (NewLogicalDeviceInfo->LogicalDeviceState !=
		  LogicalDeviceInfo->LogicalDeviceState)
		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
				"is now %s\n", Controller,
				LogicalDeviceNumber,
				Controller->ControllerNumber,
				LogicalDeviceNumber,
				(NewLogicalDeviceInfo->LogicalDeviceState
				 == DAC960_V2_LogicalDevice_Online
				 ? "ONLINE"
				 : NewLogicalDeviceInfo->LogicalDeviceState
				   == DAC960_V2_LogicalDevice_Critical
				   ? "CRITICAL" : "OFFLINE"));
	      if ((NewLogicalDeviceInfo->SoftErrors !=
		   LogicalDeviceInfo->SoftErrors) ||
		  (NewLogicalDeviceInfo->CommandsFailed !=
		   LogicalDeviceInfo->CommandsFailed) ||
		  (NewLogicalDeviceInfo->DeferredWriteErrors !=
		   LogicalDeviceInfo->DeferredWriteErrors))
		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) Errors: "
				"Soft = %d, Failed = %d, Deferred Write = %d\n",
				Controller, LogicalDeviceNumber,
				Controller->ControllerNumber,
				LogicalDeviceNumber,
				NewLogicalDeviceInfo->SoftErrors,
				NewLogicalDeviceInfo->CommandsFailed,
				NewLogicalDeviceInfo->DeferredWriteErrors);
	      if (NewLogicalDeviceInfo->ConsistencyCheckInProgress)
		DAC960_V2_ReportProgress(Controller,
					 "Consistency Check",
					 LogicalDeviceNumber,
					 NewLogicalDeviceInfo
					 ->ConsistencyCheckBlockNumber,
					 LogicalDeviceSize);
	      else if (NewLogicalDeviceInfo->RebuildInProgress)
		DAC960_V2_ReportProgress(Controller,
					 "Rebuild",
					 LogicalDeviceNumber,
					 NewLogicalDeviceInfo
					 ->RebuildBlockNumber,
					 LogicalDeviceSize);
	      else if (NewLogicalDeviceInfo->BackgroundInitializationInProgress)
		DAC960_V2_ReportProgress(Controller,
					 "Background Initialization",
					 LogicalDeviceNumber,
					 NewLogicalDeviceInfo
					 ->BackgroundInitializationBlockNumber,
					 LogicalDeviceSize);
	      else if (NewLogicalDeviceInfo->ForegroundInitializationInProgress)
		DAC960_V2_ReportProgress(Controller,
					 "Foreground Initialization",
					 LogicalDeviceNumber,
					 NewLogicalDeviceInfo
					 ->ForegroundInitializationBlockNumber,
					 LogicalDeviceSize);
	      else if (NewLogicalDeviceInfo->DataMigrationInProgress)
		DAC960_V2_ReportProgress(Controller,
					 "Data Migration",
					 LogicalDeviceNumber,
					 NewLogicalDeviceInfo
					 ->DataMigrationBlockNumber,
					 LogicalDeviceSize);
	      else if (NewLogicalDeviceInfo->PatrolOperationInProgress)
		DAC960_V2_ReportProgress(Controller,
					 "Patrol Operation",
					 LogicalDeviceNumber,
					 NewLogicalDeviceInfo
					 ->PatrolOperationBlockNumber,
					 LogicalDeviceSize);
	      if (LogicalDeviceInfo->BackgroundInitializationInProgress &&
		  !NewLogicalDeviceInfo->BackgroundInitializationInProgress)
		DAC960_Progress("Logical Drive %d (/dev/rd/c%dd%d) "
				"Background Initialization %s\n",
				Controller,
				LogicalDeviceNumber,
				Controller->ControllerNumber,
				LogicalDeviceNumber,
				(NewLogicalDeviceInfo->LogicalDeviceControl
						      .LogicalDeviceInitialized
				 ? "Completed" : "Failed"));
	      memcpy(LogicalDeviceInfo, NewLogicalDeviceInfo,
		     sizeof(DAC960_V2_LogicalDeviceInfo_T));
	    }
	  Controller->V2.LogicalDriveFoundDuringScan
			 [LogicalDeviceNumber] = true;
	  NewLogicalDeviceInfo->LogicalDeviceNumber++;
	}
      else if (CommandOpcode == DAC960_V2_GetLogicalDeviceInfoValid)
	{
	  int LogicalDriveNumber;
	  for (LogicalDriveNumber = 0;
	       LogicalDriveNumber < DAC960_MaxLogicalDrives;
	       LogicalDriveNumber++)
	    {
	      DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
		Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
	      if (LogicalDeviceInfo == NULL ||
		  Controller->V2.LogicalDriveFoundDuringScan
				 [LogicalDriveNumber])
		continue;
	      DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
			      "No Longer Exists\n", Controller,
			      LogicalDriveNumber,
			      Controller->ControllerNumber,
			      LogicalDriveNumber);
	      Controller->V2.LogicalDeviceInformation
			     [LogicalDriveNumber] = NULL;
	      kfree(LogicalDeviceInfo);
	      Controller->LogicalDriveInitiallyAccessible
			  [LogicalDriveNumber] = false;
	      DAC960_ComputeGenericDiskInfo(Controller);
	    }
	  Controller->V2.NeedLogicalDeviceInformation = false;
	}
      else if (CommandOpcode == DAC960_V2_SCSI_10_Passthru)
        {
	    DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
		Controller->V2.InquiryUnitSerialNumber[Controller->V2.PhysicalDeviceIndex - 1];

	    if (CommandStatus != DAC960_V2_NormalCompletion) {
		memset(InquiryUnitSerialNumber,
			0, sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
		InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
	    } else
	  	memcpy(InquiryUnitSerialNumber,
			Controller->V2.NewInquiryUnitSerialNumber,
			sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));

	     Controller->V2.NeedDeviceSerialNumberInformation = false;
        }

      if (Controller->V2.HealthStatusBuffer->NextEventSequenceNumber
	  - Controller->V2.NextEventSequenceNumber > 0)
	{
	  CommandMailbox->GetEvent.CommandOpcode = DAC960_V2_IOCTL;
	  CommandMailbox->GetEvent.DataTransferSize = sizeof(DAC960_V2_Event_T);
	  CommandMailbox->GetEvent.EventSequenceNumberHigh16 =
	    Controller->V2.NextEventSequenceNumber >> 16;
	  CommandMailbox->GetEvent.ControllerNumber = 0;
	  CommandMailbox->GetEvent.IOCTL_Opcode =
	    DAC960_V2_GetEvent;
	  CommandMailbox->GetEvent.EventSequenceNumberLow16 =
	    Controller->V2.NextEventSequenceNumber & 0xFFFF;
	  CommandMailbox->GetEvent.DataTransferMemoryAddress
				  .ScatterGatherSegments[0]
				  .SegmentDataPointer =
	    Controller->V2.EventDMA;
	  CommandMailbox->GetEvent.DataTransferMemoryAddress
				  .ScatterGatherSegments[0]
				  .SegmentByteCount =
	    CommandMailbox->GetEvent.DataTransferSize;
	  DAC960_QueueCommand(Command);
	  return;
	}
      if (Controller->V2.NeedPhysicalDeviceInformation)
	{
	  if (Controller->V2.NeedDeviceSerialNumberInformation)
	    {
	      DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
                Controller->V2.NewInquiryUnitSerialNumber;
	      InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;

	      DAC960_V2_ConstructNewUnitSerialNumber(Controller, CommandMailbox,
			Controller->V2.NewPhysicalDeviceInformation->Channel,
			Controller->V2.NewPhysicalDeviceInformation->TargetID,
		Controller->V2.NewPhysicalDeviceInformation->LogicalUnit - 1);


	      DAC960_QueueCommand(Command);
	      return;
	    }
	  if (Controller->V2.StartPhysicalDeviceInformationScan)
	    {
	      Controller->V2.PhysicalDeviceIndex = 0;
	      Controller->V2.NewPhysicalDeviceInformation->Channel = 0;
	      Controller->V2.NewPhysicalDeviceInformation->TargetID = 0;
	      Controller->V2.NewPhysicalDeviceInformation->LogicalUnit = 0;
	      Controller->V2.StartPhysicalDeviceInformationScan = false;
	    }
	  CommandMailbox->PhysicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL;
	  CommandMailbox->PhysicalDeviceInfo.DataTransferSize =
	    sizeof(DAC960_V2_PhysicalDeviceInfo_T);
	  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.LogicalUnit =
	    Controller->V2.NewPhysicalDeviceInformation->LogicalUnit;
	  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.TargetID =
	    Controller->V2.NewPhysicalDeviceInformation->TargetID;
	  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.Channel =
	    Controller->V2.NewPhysicalDeviceInformation->Channel;
	  CommandMailbox->PhysicalDeviceInfo.IOCTL_Opcode =
	    DAC960_V2_GetPhysicalDeviceInfoValid;
	  CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress
					    .ScatterGatherSegments[0]
					    .SegmentDataPointer =
	    Controller->V2.NewPhysicalDeviceInformationDMA;
	  CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress
					    .ScatterGatherSegments[0]
					    .SegmentByteCount =
	    CommandMailbox->PhysicalDeviceInfo.DataTransferSize;
	  DAC960_QueueCommand(Command);
	  return;
	}
      if (Controller->V2.NeedLogicalDeviceInformation)
	{
	  if (Controller->V2.StartLogicalDeviceInformationScan)
	    {
	      int LogicalDriveNumber;
	      for (LogicalDriveNumber = 0;
		   LogicalDriveNumber < DAC960_MaxLogicalDrives;
		   LogicalDriveNumber++)
		Controller->V2.LogicalDriveFoundDuringScan
			       [LogicalDriveNumber] = false;
	      Controller->V2.NewLogicalDeviceInformation->LogicalDeviceNumber = 0;
	      Controller->V2.StartLogicalDeviceInformationScan = false;
	    }
	  CommandMailbox->LogicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL;
	  CommandMailbox->LogicalDeviceInfo.DataTransferSize =
	    sizeof(DAC960_V2_LogicalDeviceInfo_T);
	  CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber =
	    Controller->V2.NewLogicalDeviceInformation->LogicalDeviceNumber;
	  CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode =
	    DAC960_V2_GetLogicalDeviceInfoValid;
	  CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress
					   .ScatterGatherSegments[0]
					   .SegmentDataPointer =
	    Controller->V2.NewLogicalDeviceInformationDMA;
	  CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress
					   .ScatterGatherSegments[0]
					   .SegmentByteCount =
	    CommandMailbox->LogicalDeviceInfo.DataTransferSize;
	  DAC960_QueueCommand(Command);
	  return;
	}
      Controller->MonitoringTimerCount++;
      Controller->MonitoringTimer.expires =
	jiffies + DAC960_HealthStatusMonitoringInterval;
      	add_timer(&Controller->MonitoringTimer);
    }
  if (CommandType == DAC960_ImmediateCommand)
    {
      complete(Command->Completion);
      Command->Completion = NULL;
      return;
    }
  if (CommandType == DAC960_QueuedCommand)
    {
      DAC960_V2_KernelCommand_T *KernelCommand = Command->V2.KernelCommand;
      KernelCommand->CommandStatus = CommandStatus;
      KernelCommand->RequestSenseLength = Command->V2.RequestSenseLength;
      KernelCommand->DataTransferLength = Command->V2.DataTransferResidue;
      Command->V2.KernelCommand = NULL;
      DAC960_DeallocateCommand(Command);
      KernelCommand->CompletionFunction(KernelCommand);
      return;
    }
  /*
    Queue a Status Monitoring Command to the Controller using the just
    completed Command if one was deferred previously due to lack of a
    free Command when the Monitoring Timer Function was called.
  */
  if (Controller->MonitoringCommandDeferred)
    {
      Controller->MonitoringCommandDeferred = false;
      DAC960_V2_QueueMonitoringCommand(Command);
      return;
    }
  /*
    Deallocate the Command.
  */
  DAC960_DeallocateCommand(Command);
  /*
    Wake up any processes waiting on a free Command.
  */
  wake_up(&Controller->CommandWaitQueue);
}

/*
  DAC960_GEM_InterruptHandler handles hardware interrupts from DAC960 GEM Series
  Controllers.
*/

static irqreturn_t DAC960_GEM_InterruptHandler(int IRQ_Channel,
				       void *DeviceIdentifier)
{
  DAC960_Controller_T *Controller = DeviceIdentifier;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V2_StatusMailbox_T *NextStatusMailbox;
  unsigned long flags;

  spin_lock_irqsave(&Controller->queue_lock, flags);
  DAC960_GEM_AcknowledgeInterrupt(ControllerBaseAddress);
  NextStatusMailbox = Controller->V2.NextStatusMailbox;
  while (NextStatusMailbox->Fields.CommandIdentifier > 0)
    {
       DAC960_V2_CommandIdentifier_T CommandIdentifier =
           NextStatusMailbox->Fields.CommandIdentifier;
       DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
       Command->V2.CommandStatus = NextStatusMailbox->Fields.CommandStatus;
       Command->V2.RequestSenseLength =
           NextStatusMailbox->Fields.RequestSenseLength;
       Command->V2.DataTransferResidue =
           NextStatusMailbox->Fields.DataTransferResidue;
       NextStatusMailbox->Words[0] = 0;
       if (++NextStatusMailbox > Controller->V2.LastStatusMailbox)
           NextStatusMailbox = Controller->V2.FirstStatusMailbox;
       DAC960_V2_ProcessCompletedCommand(Command);
    }
  Controller->V2.NextStatusMailbox = NextStatusMailbox;
  /*
    Attempt to remove additional I/O Requests from the Controller's
    I/O Request Queue and queue them to the Controller.
  */
  DAC960_ProcessRequest(Controller);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  return IRQ_HANDLED;
}

/*
  DAC960_BA_InterruptHandler handles hardware interrupts from DAC960 BA Series
  Controllers.
*/

static irqreturn_t DAC960_BA_InterruptHandler(int IRQ_Channel,
				       void *DeviceIdentifier)
{
  DAC960_Controller_T *Controller = DeviceIdentifier;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V2_StatusMailbox_T *NextStatusMailbox;
  unsigned long flags;

  spin_lock_irqsave(&Controller->queue_lock, flags);
  DAC960_BA_AcknowledgeInterrupt(ControllerBaseAddress);
  NextStatusMailbox = Controller->V2.NextStatusMailbox;
  while (NextStatusMailbox->Fields.CommandIdentifier > 0)
    {
      DAC960_V2_CommandIdentifier_T CommandIdentifier =
	NextStatusMailbox->Fields.CommandIdentifier;
      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
      Command->V2.CommandStatus = NextStatusMailbox->Fields.CommandStatus;
      Command->V2.RequestSenseLength =
	NextStatusMailbox->Fields.RequestSenseLength;
      Command->V2.DataTransferResidue =
	NextStatusMailbox->Fields.DataTransferResidue;
      NextStatusMailbox->Words[0] = 0;
      if (++NextStatusMailbox > Controller->V2.LastStatusMailbox)
	NextStatusMailbox = Controller->V2.FirstStatusMailbox;
      DAC960_V2_ProcessCompletedCommand(Command);
    }
  Controller->V2.NextStatusMailbox = NextStatusMailbox;
  /*
    Attempt to remove additional I/O Requests from the Controller's
    I/O Request Queue and queue them to the Controller.
  */
  DAC960_ProcessRequest(Controller);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  return IRQ_HANDLED;
}


/*
  DAC960_LP_InterruptHandler handles hardware interrupts from DAC960 LP Series
  Controllers.
*/

static irqreturn_t DAC960_LP_InterruptHandler(int IRQ_Channel,
				       void *DeviceIdentifier)
{
  DAC960_Controller_T *Controller = DeviceIdentifier;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V2_StatusMailbox_T *NextStatusMailbox;
  unsigned long flags;

  spin_lock_irqsave(&Controller->queue_lock, flags);
  DAC960_LP_AcknowledgeInterrupt(ControllerBaseAddress);
  NextStatusMailbox = Controller->V2.NextStatusMailbox;
  while (NextStatusMailbox->Fields.CommandIdentifier > 0)
    {
      DAC960_V2_CommandIdentifier_T CommandIdentifier =
	NextStatusMailbox->Fields.CommandIdentifier;
      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
      Command->V2.CommandStatus = NextStatusMailbox->Fields.CommandStatus;
      Command->V2.RequestSenseLength =
	NextStatusMailbox->Fields.RequestSenseLength;
      Command->V2.DataTransferResidue =
	NextStatusMailbox->Fields.DataTransferResidue;
      NextStatusMailbox->Words[0] = 0;
      if (++NextStatusMailbox > Controller->V2.LastStatusMailbox)
	NextStatusMailbox = Controller->V2.FirstStatusMailbox;
      DAC960_V2_ProcessCompletedCommand(Command);
    }
  Controller->V2.NextStatusMailbox = NextStatusMailbox;
  /*
    Attempt to remove additional I/O Requests from the Controller's
    I/O Request Queue and queue them to the Controller.
  */
  DAC960_ProcessRequest(Controller);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  return IRQ_HANDLED;
}


/*
  DAC960_LA_InterruptHandler handles hardware interrupts from DAC960 LA Series
  Controllers.
*/

static irqreturn_t DAC960_LA_InterruptHandler(int IRQ_Channel,
				       void *DeviceIdentifier)
{
  DAC960_Controller_T *Controller = DeviceIdentifier;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V1_StatusMailbox_T *NextStatusMailbox;
  unsigned long flags;

  spin_lock_irqsave(&Controller->queue_lock, flags);
  DAC960_LA_AcknowledgeInterrupt(ControllerBaseAddress);
  NextStatusMailbox = Controller->V1.NextStatusMailbox;
  while (NextStatusMailbox->Fields.Valid)
    {
      DAC960_V1_CommandIdentifier_T CommandIdentifier =
	NextStatusMailbox->Fields.CommandIdentifier;
      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
      Command->V1.CommandStatus = NextStatusMailbox->Fields.CommandStatus;
      NextStatusMailbox->Word = 0;
      if (++NextStatusMailbox > Controller->V1.LastStatusMailbox)
	NextStatusMailbox = Controller->V1.FirstStatusMailbox;
      DAC960_V1_ProcessCompletedCommand(Command);
    }
  Controller->V1.NextStatusMailbox = NextStatusMailbox;
  /*
    Attempt to remove additional I/O Requests from the Controller's
    I/O Request Queue and queue them to the Controller.
  */
  DAC960_ProcessRequest(Controller);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  return IRQ_HANDLED;
}


/*
  DAC960_PG_InterruptHandler handles hardware interrupts from DAC960 PG Series
  Controllers.
*/

static irqreturn_t DAC960_PG_InterruptHandler(int IRQ_Channel,
				       void *DeviceIdentifier)
{
  DAC960_Controller_T *Controller = DeviceIdentifier;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  DAC960_V1_StatusMailbox_T *NextStatusMailbox;
  unsigned long flags;

  spin_lock_irqsave(&Controller->queue_lock, flags);
  DAC960_PG_AcknowledgeInterrupt(ControllerBaseAddress);
  NextStatusMailbox = Controller->V1.NextStatusMailbox;
  while (NextStatusMailbox->Fields.Valid)
    {
      DAC960_V1_CommandIdentifier_T CommandIdentifier =
	NextStatusMailbox->Fields.CommandIdentifier;
      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
      Command->V1.CommandStatus = NextStatusMailbox->Fields.CommandStatus;
      NextStatusMailbox->Word = 0;
      if (++NextStatusMailbox > Controller->V1.LastStatusMailbox)
	NextStatusMailbox = Controller->V1.FirstStatusMailbox;
      DAC960_V1_ProcessCompletedCommand(Command);
    }
  Controller->V1.NextStatusMailbox = NextStatusMailbox;
  /*
    Attempt to remove additional I/O Requests from the Controller's
    I/O Request Queue and queue them to the Controller.
  */
  DAC960_ProcessRequest(Controller);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  return IRQ_HANDLED;
}


/*
  DAC960_PD_InterruptHandler handles hardware interrupts from DAC960 PD Series
  Controllers.
*/

static irqreturn_t DAC960_PD_InterruptHandler(int IRQ_Channel,
				       void *DeviceIdentifier)
{
  DAC960_Controller_T *Controller = DeviceIdentifier;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  unsigned long flags;

  spin_lock_irqsave(&Controller->queue_lock, flags);
  while (DAC960_PD_StatusAvailableP(ControllerBaseAddress))
    {
      DAC960_V1_CommandIdentifier_T CommandIdentifier =
	DAC960_PD_ReadStatusCommandIdentifier(ControllerBaseAddress);
      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
      Command->V1.CommandStatus =
	DAC960_PD_ReadStatusRegister(ControllerBaseAddress);
      DAC960_PD_AcknowledgeInterrupt(ControllerBaseAddress);
      DAC960_PD_AcknowledgeStatus(ControllerBaseAddress);
      DAC960_V1_ProcessCompletedCommand(Command);
    }
  /*
    Attempt to remove additional I/O Requests from the Controller's
    I/O Request Queue and queue them to the Controller.
  */
  DAC960_ProcessRequest(Controller);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  return IRQ_HANDLED;
}


/*
  DAC960_P_InterruptHandler handles hardware interrupts from DAC960 P Series
  Controllers.

  Translations of DAC960_V1_Enquiry and DAC960_V1_GetDeviceState rely
  on the data having been placed into DAC960_Controller_T, rather than
  an arbitrary buffer.
*/

static irqreturn_t DAC960_P_InterruptHandler(int IRQ_Channel,
				      void *DeviceIdentifier)
{
  DAC960_Controller_T *Controller = DeviceIdentifier;
  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
  unsigned long flags;

  spin_lock_irqsave(&Controller->queue_lock, flags);
  while (DAC960_PD_StatusAvailableP(ControllerBaseAddress))
    {
      DAC960_V1_CommandIdentifier_T CommandIdentifier =
	DAC960_PD_ReadStatusCommandIdentifier(ControllerBaseAddress);
      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
      DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
      DAC960_V1_CommandOpcode_T CommandOpcode =
	CommandMailbox->Common.CommandOpcode;
      Command->V1.CommandStatus =
	DAC960_PD_ReadStatusRegister(ControllerBaseAddress);
      DAC960_PD_AcknowledgeInterrupt(ControllerBaseAddress);
      DAC960_PD_AcknowledgeStatus(ControllerBaseAddress);
      switch (CommandOpcode)
	{
	case DAC960_V1_Enquiry_Old:
	  Command->V1.CommandMailbox.Common.CommandOpcode = DAC960_V1_Enquiry;
	  DAC960_P_To_PD_TranslateEnquiry(Controller->V1.NewEnquiry);
	  break;
	case DAC960_V1_GetDeviceState_Old:
	  Command->V1.CommandMailbox.Common.CommandOpcode =
	    					DAC960_V1_GetDeviceState;
	  DAC960_P_To_PD_TranslateDeviceState(Controller->V1.NewDeviceState);
	  break;
	case DAC960_V1_Read_Old:
	  Command->V1.CommandMailbox.Common.CommandOpcode = DAC960_V1_Read;
	  DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
	  break;
	case DAC960_V1_Write_Old:
	  Command->V1.CommandMailbox.Common.CommandOpcode = DAC960_V1_Write;
	  DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
	  break;
	case DAC960_V1_ReadWithScatterGather_Old:
	  Command->V1.CommandMailbox.Common.CommandOpcode =
	    DAC960_V1_ReadWithScatterGather;
	  DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
	  break;
	case DAC960_V1_WriteWithScatterGather_Old:
	  Command->V1.CommandMailbox.Common.CommandOpcode =
	    DAC960_V1_WriteWithScatterGather;
	  DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
	  break;
	default:
	  break;
	}
      DAC960_V1_ProcessCompletedCommand(Command);
    }
  /*
    Attempt to remove additional I/O Requests from the Controller's
    I/O Request Queue and queue them to the Controller.
  */
  DAC960_ProcessRequest(Controller);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  return IRQ_HANDLED;
}


/*
  DAC960_V1_QueueMonitoringCommand queues a Monitoring Command to DAC960 V1
  Firmware Controllers.
*/

static void DAC960_V1_QueueMonitoringCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  DAC960_V1_ClearCommand(Command);
  Command->CommandType = DAC960_MonitoringCommand;
  CommandMailbox->Type3.CommandOpcode = DAC960_V1_Enquiry;
  CommandMailbox->Type3.BusAddress = Controller->V1.NewEnquiryDMA;
  DAC960_QueueCommand(Command);
}


/*
  DAC960_V2_QueueMonitoringCommand queues a Monitoring Command to DAC960 V2
  Firmware Controllers.
*/

static void DAC960_V2_QueueMonitoringCommand(DAC960_Command_T *Command)
{
  DAC960_Controller_T *Controller = Command->Controller;
  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
  DAC960_V2_ClearCommand(Command);
  Command->CommandType = DAC960_MonitoringCommand;
  CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL;
  CommandMailbox->ControllerInfo.CommandControlBits
				.DataTransferControllerToHost = true;
  CommandMailbox->ControllerInfo.CommandControlBits
				.NoAutoRequestSense = true;
  CommandMailbox->ControllerInfo.DataTransferSize =
    sizeof(DAC960_V2_ControllerInfo_T);
  CommandMailbox->ControllerInfo.ControllerNumber = 0;
  CommandMailbox->ControllerInfo.IOCTL_Opcode = DAC960_V2_GetControllerInfo;
  CommandMailbox->ControllerInfo.DataTransferMemoryAddress
				.ScatterGatherSegments[0]
				.SegmentDataPointer =
    Controller->V2.NewControllerInformationDMA;
  CommandMailbox->ControllerInfo.DataTransferMemoryAddress
				.ScatterGatherSegments[0]
				.SegmentByteCount =
    CommandMailbox->ControllerInfo.DataTransferSize;
  DAC960_QueueCommand(Command);
}


/*
  DAC960_MonitoringTimerFunction is the timer function for monitoring
  the status of DAC960 Controllers.
*/

static void DAC960_MonitoringTimerFunction(unsigned long TimerData)
{
  DAC960_Controller_T *Controller = (DAC960_Controller_T *) TimerData;
  DAC960_Command_T *Command;
  unsigned long flags;

  if (Controller->FirmwareType == DAC960_V1_Controller)
    {
      spin_lock_irqsave(&Controller->queue_lock, flags);
      /*
	Queue a Status Monitoring Command to Controller.
      */
      Command = DAC960_AllocateCommand(Controller);
      if (Command != NULL)
	DAC960_V1_QueueMonitoringCommand(Command);
      else Controller->MonitoringCommandDeferred = true;
      spin_unlock_irqrestore(&Controller->queue_lock, flags);
    }
  else
    {
      DAC960_V2_ControllerInfo_T *ControllerInfo =
	&Controller->V2.ControllerInformation;
      unsigned int StatusChangeCounter =
	Controller->V2.HealthStatusBuffer->StatusChangeCounter;
      bool ForceMonitoringCommand = false;
      if (time_after(jiffies, Controller->SecondaryMonitoringTime
	  + DAC960_SecondaryMonitoringInterval))
	{
	  int LogicalDriveNumber;
	  for (LogicalDriveNumber = 0;
	       LogicalDriveNumber < DAC960_MaxLogicalDrives;
	       LogicalDriveNumber++)
	    {
	      DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
		Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
	      if (LogicalDeviceInfo == NULL) continue;
	      if (!LogicalDeviceInfo->LogicalDeviceControl
				     .LogicalDeviceInitialized)
		{
		  ForceMonitoringCommand = true;
		  break;
		}
	    }
	  Controller->SecondaryMonitoringTime = jiffies;
	}
      if (StatusChangeCounter == Controller->V2.StatusChangeCounter &&
	  Controller->V2.HealthStatusBuffer->NextEventSequenceNumber
	  == Controller->V2.NextEventSequenceNumber &&
	  (ControllerInfo->BackgroundInitializationsActive +
	   ControllerInfo->LogicalDeviceInitializationsActive +
	   ControllerInfo->PhysicalDeviceInitializationsActive +
	   ControllerInfo->ConsistencyChecksActive +
	   ControllerInfo->RebuildsActive +
	   ControllerInfo->OnlineExpansionsActive == 0 ||
	   time_before(jiffies, Controller->PrimaryMonitoringTime
	   + DAC960_MonitoringTimerInterval)) &&
	  !ForceMonitoringCommand)
	{
	  Controller->MonitoringTimer.expires =
	    jiffies + DAC960_HealthStatusMonitoringInterval;
	    add_timer(&Controller->MonitoringTimer);
	  return;
	}
      Controller->V2.StatusChangeCounter = StatusChangeCounter;
      Controller->PrimaryMonitoringTime = jiffies;

      spin_lock_irqsave(&Controller->queue_lock, flags);
      /*
	Queue a Status Monitoring Command to Controller.
      */
      Command = DAC960_AllocateCommand(Controller);
      if (Command != NULL)
	DAC960_V2_QueueMonitoringCommand(Command);
      else Controller->MonitoringCommandDeferred = true;
      spin_unlock_irqrestore(&Controller->queue_lock, flags);
      /*
	Wake up any processes waiting on a Health Status Buffer change.
      */
      wake_up(&Controller->HealthStatusWaitQueue);
    }
}

/*
  DAC960_CheckStatusBuffer verifies that there is room to hold ByteCount
  additional bytes in the Combined Status Buffer and grows the buffer if
  necessary.  It returns true if there is enough room and false otherwise.
*/

static bool DAC960_CheckStatusBuffer(DAC960_Controller_T *Controller,
					unsigned int ByteCount)
{
  unsigned char *NewStatusBuffer;
  if (Controller->InitialStatusLength + 1 +
      Controller->CurrentStatusLength + ByteCount + 1 <=
      Controller->CombinedStatusBufferLength)
    return true;
  if (Controller->CombinedStatusBufferLength == 0)
    {
      unsigned int NewStatusBufferLength = DAC960_InitialStatusBufferSize;
      while (NewStatusBufferLength < ByteCount)
	NewStatusBufferLength *= 2;
      Controller->CombinedStatusBuffer = kmalloc(NewStatusBufferLength,
						  GFP_ATOMIC);
      if (Controller->CombinedStatusBuffer == NULL) return false;
      Controller->CombinedStatusBufferLength = NewStatusBufferLength;
      return true;
    }
  NewStatusBuffer = kmalloc(2 * Controller->CombinedStatusBufferLength,
			     GFP_ATOMIC);
  if (NewStatusBuffer == NULL)
    {
      DAC960_Warning("Unable to expand Combined Status Buffer - Truncating\n",
		     Controller);
      return false;
    }
  memcpy(NewStatusBuffer, Controller->CombinedStatusBuffer,
	 Controller->CombinedStatusBufferLength);
  kfree(Controller->CombinedStatusBuffer);
  Controller->CombinedStatusBuffer = NewStatusBuffer;
  Controller->CombinedStatusBufferLength *= 2;
  Controller->CurrentStatusBuffer =
    &NewStatusBuffer[Controller->InitialStatusLength + 1];
  return true;
}


/*
  DAC960_Message prints Driver Messages.
*/

static void DAC960_Message(DAC960_MessageLevel_T MessageLevel,
			   unsigned char *Format,
			   DAC960_Controller_T *Controller,
			   ...)
{
  static unsigned char Buffer[DAC960_LineBufferSize];
  static bool BeginningOfLine = true;
  va_list Arguments;
  int Length = 0;
  va_start(Arguments, Controller);
  Length = vsprintf(Buffer, Format, Arguments);
  va_end(Arguments);
  if (Controller == NULL)
    printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
	   DAC960_ControllerCount, Buffer);
  else if (MessageLevel == DAC960_AnnounceLevel ||
	   MessageLevel == DAC960_InfoLevel)
    {
      if (!Controller->ControllerInitialized)
	{
	  if (DAC960_CheckStatusBuffer(Controller, Length))
	    {
	      strcpy(&Controller->CombinedStatusBuffer
				  [Controller->InitialStatusLength],
		     Buffer);
	      Controller->InitialStatusLength += Length;
	      Controller->CurrentStatusBuffer =
		&Controller->CombinedStatusBuffer
			     [Controller->InitialStatusLength + 1];
	    }
	  if (MessageLevel == DAC960_AnnounceLevel)
	    {
	      static int AnnouncementLines = 0;
	      if (++AnnouncementLines <= 2)
		printk("%sDAC960: %s", DAC960_MessageLevelMap[MessageLevel],
		       Buffer);
	    }
	  else
	    {
	      if (BeginningOfLine)
		{
		  if (Buffer[0] != '\n' || Length > 1)
		    printk("%sDAC960#%d: %s",
			   DAC960_MessageLevelMap[MessageLevel],
			   Controller->ControllerNumber, Buffer);
		}
	      else printk("%s", Buffer);
	    }
	}
      else if (DAC960_CheckStatusBuffer(Controller, Length))
	{
	  strcpy(&Controller->CurrentStatusBuffer[
		    Controller->CurrentStatusLength], Buffer);
	  Controller->CurrentStatusLength += Length;
	}
    }
  else if (MessageLevel == DAC960_ProgressLevel)
    {
      strcpy(Controller->ProgressBuffer, Buffer);
      Controller->ProgressBufferLength = Length;
      if (Controller->EphemeralProgressMessage)
	{
	  if (time_after_eq(jiffies, Controller->LastProgressReportTime
	      + DAC960_ProgressReportingInterval))
	    {
	      printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
		     Controller->ControllerNumber, Buffer);
	      Controller->LastProgressReportTime = jiffies;
	    }
	}
      else printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
		  Controller->ControllerNumber, Buffer);
    }
  else if (MessageLevel == DAC960_UserCriticalLevel)
    {
      strcpy(&Controller->UserStatusBuffer[Controller->UserStatusLength],
	     Buffer);
      Controller->UserStatusLength += Length;
      if (Buffer[0] != '\n' || Length > 1)
	printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
	       Controller->ControllerNumber, Buffer);
    }
  else
    {
      if (BeginningOfLine)
	printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
	       Controller->ControllerNumber, Buffer);
      else printk("%s", Buffer);
    }
  BeginningOfLine = (Buffer[Length-1] == '\n');
}


/*
  DAC960_ParsePhysicalDevice parses spaces followed by a Physical Device
  Channel:TargetID specification from a User Command string.  It updates
  Channel and TargetID and returns true on success and false on failure.
*/

static bool DAC960_ParsePhysicalDevice(DAC960_Controller_T *Controller,
					  char *UserCommandString,
					  unsigned char *Channel,
					  unsigned char *TargetID)
{
  char *NewUserCommandString = UserCommandString;
  unsigned long XChannel, XTargetID;
  while (*UserCommandString == ' ') UserCommandString++;
  if (UserCommandString == NewUserCommandString)
    return false;
  XChannel = simple_strtoul(UserCommandString, &NewUserCommandString, 10);
  if (NewUserCommandString == UserCommandString ||
      *NewUserCommandString != ':' ||
      XChannel >= Controller->Channels)
    return false;
  UserCommandString = ++NewUserCommandString;
  XTargetID = simple_strtoul(UserCommandString, &NewUserCommandString, 10);
  if (NewUserCommandString == UserCommandString ||
      *NewUserCommandString != '\0' ||
      XTargetID >= Controller->Targets)
    return false;
  *Channel = XChannel;
  *TargetID = XTargetID;
  return true;
}


/*
  DAC960_ParseLogicalDrive parses spaces followed by a Logical Drive Number
  specification from a User Command string.  It updates LogicalDriveNumber and
  returns true on success and false on failure.
*/

static bool DAC960_ParseLogicalDrive(DAC960_Controller_T *Controller,
					char *UserCommandString,
					unsigned char *LogicalDriveNumber)
{
  char *NewUserCommandString = UserCommandString;
  unsigned long XLogicalDriveNumber;
  while (*UserCommandString == ' ') UserCommandString++;
  if (UserCommandString == NewUserCommandString)
    return false;
  XLogicalDriveNumber =
    simple_strtoul(UserCommandString, &NewUserCommandString, 10);
  if (NewUserCommandString == UserCommandString ||
      *NewUserCommandString != '\0' ||
      XLogicalDriveNumber > DAC960_MaxLogicalDrives - 1)
    return false;
  *LogicalDriveNumber = XLogicalDriveNumber;
  return true;
}


/*
  DAC960_V1_SetDeviceState sets the Device State for a Physical Device for
  DAC960 V1 Firmware Controllers.
*/

static void DAC960_V1_SetDeviceState(DAC960_Controller_T *Controller,
				     DAC960_Command_T *Command,
				     unsigned char Channel,
				     unsigned char TargetID,
				     DAC960_V1_PhysicalDeviceState_T
				       DeviceState,
				     const unsigned char *DeviceStateString)
{
  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
  CommandMailbox->Type3D.CommandOpcode = DAC960_V1_StartDevice;
  CommandMailbox->Type3D.Channel = Channel;
  CommandMailbox->Type3D.TargetID = TargetID;
  CommandMailbox->Type3D.DeviceState = DeviceState;
  CommandMailbox->Type3D.Modifier = 0;
  DAC960_ExecuteCommand(Command);
  switch (Command->V1.CommandStatus)
    {
    case DAC960_V1_NormalCompletion:
      DAC960_UserCritical("%s of Physical Device %d:%d Succeeded\n", Controller,
			  DeviceStateString, Channel, TargetID);
      break;
    case DAC960_V1_UnableToStartDevice:
      DAC960_UserCritical("%s of Physical Device %d:%d Failed - "
			  "Unable to Start Device\n", Controller,
			  DeviceStateString, Channel, TargetID);
      break;
    case DAC960_V1_NoDeviceAtAddress:
      DAC960_UserCritical("%s of Physical Device %d:%d Failed - "
			  "No Device at Address\n", Controller,
			  DeviceStateString, Channel, TargetID);
      break;
    case DAC960_V1_InvalidChannelOrTargetOrModifier:
      DAC960_UserCritical("%s of Physical Device %d:%d Failed - "
			  "Invalid Channel or Target or Modifier\n",
			  Controller, DeviceStateString, Channel, TargetID);
      break;
    case DAC960_V1_ChannelBusy:
      DAC960_UserCritical("%s of Physical Device %d:%d Failed - "
			  "Channel Busy\n", Controller,
			  DeviceStateString, Channel, TargetID);
      break;
    default:
      DAC960_UserCritical("%s of Physical Device %d:%d Failed - "
			  "Unexpected Status %04X\n", Controller,
			  DeviceStateString, Channel, TargetID,
			  Command->V1.CommandStatus);
      break;
    }
}


/*
  DAC960_V1_ExecuteUserCommand executes a User Command for DAC960 V1 Firmware
  Controllers.
*/

static bool DAC960_V1_ExecuteUserCommand(DAC960_Controller_T *Controller,
					    unsigned char *UserCommand)
{
  DAC960_Command_T *Command;
  DAC960_V1_CommandMailbox_T *CommandMailbox;
  unsigned long flags;
  unsigned char Channel, TargetID, LogicalDriveNumber;

  spin_lock_irqsave(&Controller->queue_lock, flags);
  while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
    DAC960_WaitForCommand(Controller);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  Controller->UserStatusLength = 0;
  DAC960_V1_ClearCommand(Command);
  Command->CommandType = DAC960_ImmediateCommand;
  CommandMailbox = &Command->V1.CommandMailbox;
  if (strcmp(UserCommand, "flush-cache") == 0)
    {
      CommandMailbox->Type3.CommandOpcode = DAC960_V1_Flush;
      DAC960_ExecuteCommand(Command);
      DAC960_UserCritical("Cache Flush Completed\n", Controller);
    }
  else if (strncmp(UserCommand, "kill", 4) == 0 &&
	   DAC960_ParsePhysicalDevice(Controller, &UserCommand[4],
				      &Channel, &TargetID))
    {
      DAC960_V1_DeviceState_T *DeviceState =
	&Controller->V1.DeviceState[Channel][TargetID];
      if (DeviceState->Present &&
	  DeviceState->DeviceType == DAC960_V1_DiskType &&
	  DeviceState->DeviceState != DAC960_V1_Device_Dead)
	DAC960_V1_SetDeviceState(Controller, Command, Channel, TargetID,
				 DAC960_V1_Device_Dead, "Kill");
      else DAC960_UserCritical("Kill of Physical Device %d:%d Illegal\n",
			       Controller, Channel, TargetID);
    }
  else if (strncmp(UserCommand, "make-online", 11) == 0 &&
	   DAC960_ParsePhysicalDevice(Controller, &UserCommand[11],
				      &Channel, &TargetID))
    {
      DAC960_V1_DeviceState_T *DeviceState =
	&Controller->V1.DeviceState[Channel][TargetID];
      if (DeviceState->Present &&
	  DeviceState->DeviceType == DAC960_V1_DiskType &&
	  DeviceState->DeviceState == DAC960_V1_Device_Dead)
	DAC960_V1_SetDeviceState(Controller, Command, Channel, TargetID,
				 DAC960_V1_Device_Online, "Make Online");
      else DAC960_UserCritical("Make Online of Physical Device %d:%d Illegal\n",
			       Controller, Channel, TargetID);

    }
  else if (strncmp(UserCommand, "make-standby", 12) == 0 &&
	   DAC960_ParsePhysicalDevice(Controller, &UserCommand[12],
				      &Channel, &TargetID))
    {
      DAC960_V1_DeviceState_T *DeviceState =
	&Controller->V1.DeviceState[Channel][TargetID];
      if (DeviceState->Present &&
	  DeviceState->DeviceType == DAC960_V1_DiskType &&
	  DeviceState->DeviceState == DAC960_V1_Device_Dead)
	DAC960_V1_SetDeviceState(Controller, Command, Channel, TargetID,
				 DAC960_V1_Device_Standby, "Make Standby");
      else DAC960_UserCritical("Make Standby of Physical "
			       "Device %d:%d Illegal\n",
			       Controller, Channel, TargetID);
    }
  else if (strncmp(UserCommand, "rebuild", 7) == 0 &&
	   DAC960_ParsePhysicalDevice(Controller, &UserCommand[7],
				      &Channel, &TargetID))
    {
      CommandMailbox->Type3D.CommandOpcode = DAC960_V1_RebuildAsync;
      CommandMailbox->Type3D.Channel = Channel;
      CommandMailbox->Type3D.TargetID = TargetID;
      DAC960_ExecuteCommand(Command);
      switch (Command->V1.CommandStatus)
	{
	case DAC960_V1_NormalCompletion:
	  DAC960_UserCritical("Rebuild of Physical Device %d:%d Initiated\n",
			      Controller, Channel, TargetID);
	  break;
	case DAC960_V1_AttemptToRebuildOnlineDrive:
	  DAC960_UserCritical("Rebuild of Physical Device %d:%d Failed - "
			      "Attempt to Rebuild Online or "
			      "Unresponsive Drive\n",
			      Controller, Channel, TargetID);
	  break;
	case DAC960_V1_NewDiskFailedDuringRebuild:
	  DAC960_UserCritical("Rebuild of Physical Device %d:%d Failed - "
			      "New Disk Failed During Rebuild\n",
			      Controller, Channel, TargetID);
	  break;
	case DAC960_V1_InvalidDeviceAddress:
	  DAC960_UserCritical("Rebuild of Physical Device %d:%d Failed - "
			      "Invalid Device Address\n",
			      Controller, Channel, TargetID);
	  break;
	case DAC960_V1_RebuildOrCheckAlreadyInProgress:
	  DAC960_UserCritical("Rebuild of Physical Device %d:%d Failed - "
			      "Rebuild or Consistency Check Already "
			      "in Progress\n", Controller, Channel, TargetID);
	  break;
	default:
	  DAC960_UserCritical("Rebuild of Physical Device %d:%d Failed - "
			      "Unexpected Status %04X\n", Controller,
			      Channel, TargetID, Command->V1.CommandStatus);
	  break;
	}
    }
  else if (strncmp(UserCommand, "check-consistency", 17) == 0 &&
	   DAC960_ParseLogicalDrive(Controller, &UserCommand[17],
				    &LogicalDriveNumber))
    {
      CommandMailbox->Type3C.CommandOpcode = DAC960_V1_CheckConsistencyAsync;
      CommandMailbox->Type3C.LogicalDriveNumber = LogicalDriveNumber;
      CommandMailbox->Type3C.AutoRestore = true;
      DAC960_ExecuteCommand(Command);
      switch (Command->V1.CommandStatus)
	{
	case DAC960_V1_NormalCompletion:
	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
			      "(/dev/rd/c%dd%d) Initiated\n",
			      Controller, LogicalDriveNumber,
			      Controller->ControllerNumber,
			      LogicalDriveNumber);
	  break;
	case DAC960_V1_DependentDiskIsDead:
	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
			      "(/dev/rd/c%dd%d) Failed - "
			      "Dependent Physical Device is DEAD\n",
			      Controller, LogicalDriveNumber,
			      Controller->ControllerNumber,
			      LogicalDriveNumber);
	  break;
	case DAC960_V1_InvalidOrNonredundantLogicalDrive:
	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
			      "(/dev/rd/c%dd%d) Failed - "
			      "Invalid or Nonredundant Logical Drive\n",
			      Controller, LogicalDriveNumber,
			      Controller->ControllerNumber,
			      LogicalDriveNumber);
	  break;
	case DAC960_V1_RebuildOrCheckAlreadyInProgress:
	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
			      "(/dev/rd/c%dd%d) Failed - Rebuild or "
			      "Consistency Check Already in Progress\n",
			      Controller, LogicalDriveNumber,
			      Controller->ControllerNumber,
			      LogicalDriveNumber);
	  break;
	default:
	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
			      "(/dev/rd/c%dd%d) Failed - "
			      "Unexpected Status %04X\n",
			      Controller, LogicalDriveNumber,
			      Controller->ControllerNumber,
			      LogicalDriveNumber, Command->V1.CommandStatus);
	  break;
	}
    }
  else if (strcmp(UserCommand, "cancel-rebuild") == 0 ||
	   strcmp(UserCommand, "cancel-consistency-check") == 0)
    {
      /*
        the OldRebuildRateConstant is never actually used
        once its value is retrieved from the controller.
       */
      unsigned char *OldRebuildRateConstant;
      dma_addr_t OldRebuildRateConstantDMA;

      OldRebuildRateConstant = pci_alloc_consistent( Controller->PCIDevice,
		sizeof(char), &OldRebuildRateConstantDMA);
      if (OldRebuildRateConstant == NULL) {
         DAC960_UserCritical("Cancellation of Rebuild or "
			     "Consistency Check Failed - "
			     "Out of Memory",
                             Controller);
	 goto failure;
      }
      CommandMailbox->Type3R.CommandOpcode = DAC960_V1_RebuildControl;
      CommandMailbox->Type3R.RebuildRateConstant = 0xFF;
      CommandMailbox->Type3R.BusAddress = OldRebuildRateConstantDMA;
      DAC960_ExecuteCommand(Command);
      switch (Command->V1.CommandStatus)
	{
	case DAC960_V1_NormalCompletion:
	  DAC960_UserCritical("Rebuild or Consistency Check Cancelled\n",
			      Controller);
	  break;
	default:
	  DAC960_UserCritical("Cancellation of Rebuild or "
			      "Consistency Check Failed - "
			      "Unexpected Status %04X\n",
			      Controller, Command->V1.CommandStatus);
	  break;
	}
failure:
  	pci_free_consistent(Controller->PCIDevice, sizeof(char),
		OldRebuildRateConstant, OldRebuildRateConstantDMA);
    }
  else DAC960_UserCritical("Illegal User Command: '%s'\n",
			   Controller, UserCommand);

  spin_lock_irqsave(&Controller->queue_lock, flags);
  DAC960_DeallocateCommand(Command);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  return true;
}


/*
  DAC960_V2_TranslatePhysicalDevice translates a Physical Device Channel and
  TargetID into a Logical Device.  It returns true on success and false
  on failure.
*/

static bool DAC960_V2_TranslatePhysicalDevice(DAC960_Command_T *Command,
						 unsigned char Channel,
						 unsigned char TargetID,
						 unsigned short
						   *LogicalDeviceNumber)
{
  DAC960_V2_CommandMailbox_T SavedCommandMailbox, *CommandMailbox;
  DAC960_Controller_T *Controller =  Command->Controller;

  CommandMailbox = &Command->V2.CommandMailbox;
  memcpy(&SavedCommandMailbox, CommandMailbox,
	 sizeof(DAC960_V2_CommandMailbox_T));

  CommandMailbox->PhysicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL;
  CommandMailbox->PhysicalDeviceInfo.CommandControlBits
				    .DataTransferControllerToHost = true;
  CommandMailbox->PhysicalDeviceInfo.CommandControlBits
				    .NoAutoRequestSense = true;
  CommandMailbox->PhysicalDeviceInfo.DataTransferSize =
    sizeof(DAC960_V2_PhysicalToLogicalDevice_T);
  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.TargetID = TargetID;
  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.Channel = Channel;
  CommandMailbox->PhysicalDeviceInfo.IOCTL_Opcode =
    DAC960_V2_TranslatePhysicalToLogicalDevice;
  CommandMailbox->Common.DataTransferMemoryAddress
			.ScatterGatherSegments[0]
			.SegmentDataPointer =
    		Controller->V2.PhysicalToLogicalDeviceDMA;
  CommandMailbox->Common.DataTransferMemoryAddress
			.ScatterGatherSegments[0]
			.SegmentByteCount =
    		CommandMailbox->Common.DataTransferSize;

  DAC960_ExecuteCommand(Command);
  *LogicalDeviceNumber = Controller->V2.PhysicalToLogicalDevice->LogicalDeviceNumber;

  memcpy(CommandMailbox, &SavedCommandMailbox,
	 sizeof(DAC960_V2_CommandMailbox_T));
  return (Command->V2.CommandStatus == DAC960_V2_NormalCompletion);
}


/*
  DAC960_V2_ExecuteUserCommand executes a User Command for DAC960 V2 Firmware
  Controllers.
*/

static bool DAC960_V2_ExecuteUserCommand(DAC960_Controller_T *Controller,
					    unsigned char *UserCommand)
{
  DAC960_Command_T *Command;
  DAC960_V2_CommandMailbox_T *CommandMailbox;
  unsigned long flags;
  unsigned char Channel, TargetID, LogicalDriveNumber;
  unsigned short LogicalDeviceNumber;

  spin_lock_irqsave(&Controller->queue_lock, flags);
  while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
    DAC960_WaitForCommand(Controller);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  Controller->UserStatusLength = 0;
  DAC960_V2_ClearCommand(Command);
  Command->CommandType = DAC960_ImmediateCommand;
  CommandMailbox = &Command->V2.CommandMailbox;
  CommandMailbox->Common.CommandOpcode = DAC960_V2_IOCTL;
  CommandMailbox->Common.CommandControlBits.DataTransferControllerToHost = true;
  CommandMailbox->Common.CommandControlBits.NoAutoRequestSense = true;
  if (strcmp(UserCommand, "flush-cache") == 0)
    {
      CommandMailbox->DeviceOperation.IOCTL_Opcode = DAC960_V2_PauseDevice;
      CommandMailbox->DeviceOperation.OperationDevice =
	DAC960_V2_RAID_Controller;
      DAC960_ExecuteCommand(Command);
      DAC960_UserCritical("Cache Flush Completed\n", Controller);
    }
  else if (strncmp(UserCommand, "kill", 4) == 0 &&
	   DAC960_ParsePhysicalDevice(Controller, &UserCommand[4],
				      &Channel, &TargetID) &&
	   DAC960_V2_TranslatePhysicalDevice(Command, Channel, TargetID,
					     &LogicalDeviceNumber))
    {
      CommandMailbox->SetDeviceState.LogicalDevice.LogicalDeviceNumber =
	LogicalDeviceNumber;
      CommandMailbox->SetDeviceState.IOCTL_Opcode =
	DAC960_V2_SetDeviceState;
      CommandMailbox->SetDeviceState.DeviceState.PhysicalDeviceState =
	DAC960_V2_Device_Dead;
      DAC960_ExecuteCommand(Command);
      DAC960_UserCritical("Kill of Physical Device %d:%d %s\n",
			  Controller, Channel, TargetID,
			  (Command->V2.CommandStatus
			   == DAC960_V2_NormalCompletion
			   ? "Succeeded" : "Failed"));
    }
  else if (strncmp(UserCommand, "make-online", 11) == 0 &&
	   DAC960_ParsePhysicalDevice(Controller, &UserCommand[11],
				      &Channel, &TargetID) &&
	   DAC960_V2_TranslatePhysicalDevice(Command, Channel, TargetID,
					     &LogicalDeviceNumber))
    {
      CommandMailbox->SetDeviceState.LogicalDevice.LogicalDeviceNumber =
	LogicalDeviceNumber;
      CommandMailbox->SetDeviceState.IOCTL_Opcode =
	DAC960_V2_SetDeviceState;
      CommandMailbox->SetDeviceState.DeviceState.PhysicalDeviceState =
	DAC960_V2_Device_Online;
      DAC960_ExecuteCommand(Command);
      DAC960_UserCritical("Make Online of Physical Device %d:%d %s\n",
			  Controller, Channel, TargetID,
			  (Command->V2.CommandStatus
			   == DAC960_V2_NormalCompletion
			   ? "Succeeded" : "Failed"));
    }
  else if (strncmp(UserCommand, "make-standby", 12) == 0 &&
	   DAC960_ParsePhysicalDevice(Controller, &UserCommand[12],
				      &Channel, &TargetID) &&
	   DAC960_V2_TranslatePhysicalDevice(Command, Channel, TargetID,
					     &LogicalDeviceNumber))
    {
      CommandMailbox->SetDeviceState.LogicalDevice.LogicalDeviceNumber =
	LogicalDeviceNumber;
      CommandMailbox->SetDeviceState.IOCTL_Opcode =
	DAC960_V2_SetDeviceState;
      CommandMailbox->SetDeviceState.DeviceState.PhysicalDeviceState =
	DAC960_V2_Device_Standby;
      DAC960_ExecuteCommand(Command);
      DAC960_UserCritical("Make Standby of Physical Device %d:%d %s\n",
			  Controller, Channel, TargetID,
			  (Command->V2.CommandStatus
			   == DAC960_V2_NormalCompletion
			   ? "Succeeded" : "Failed"));
    }
  else if (strncmp(UserCommand, "rebuild", 7) == 0 &&
	   DAC960_ParsePhysicalDevice(Controller, &UserCommand[7],
				      &Channel, &TargetID) &&
	   DAC960_V2_TranslatePhysicalDevice(Command, Channel, TargetID,
					     &LogicalDeviceNumber))
    {
      CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber =
	LogicalDeviceNumber;
      CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode =
	DAC960_V2_RebuildDeviceStart;
      DAC960_ExecuteCommand(Command);
      DAC960_UserCritical("Rebuild of Physical Device %d:%d %s\n",
			  Controller, Channel, TargetID,
			  (Command->V2.CommandStatus
			   == DAC960_V2_NormalCompletion
			   ? "Initiated" : "Not Initiated"));
    }
  else if (strncmp(UserCommand, "cancel-rebuild", 14) == 0 &&
	   DAC960_ParsePhysicalDevice(Controller, &UserCommand[14],
				      &Channel, &TargetID) &&
	   DAC960_V2_TranslatePhysicalDevice(Command, Channel, TargetID,
					     &LogicalDeviceNumber))
    {
      CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber =
	LogicalDeviceNumber;
      CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode =
	DAC960_V2_RebuildDeviceStop;
      DAC960_ExecuteCommand(Command);
      DAC960_UserCritical("Rebuild of Physical Device %d:%d %s\n",
			  Controller, Channel, TargetID,
			  (Command->V2.CommandStatus
			   == DAC960_V2_NormalCompletion
			   ? "Cancelled" : "Not Cancelled"));
    }
  else if (strncmp(UserCommand, "check-consistency", 17) == 0 &&
	   DAC960_ParseLogicalDrive(Controller, &UserCommand[17],
				    &LogicalDriveNumber))
    {
      CommandMailbox->ConsistencyCheck.LogicalDevice.LogicalDeviceNumber =
	LogicalDriveNumber;
      CommandMailbox->ConsistencyCheck.IOCTL_Opcode =
	DAC960_V2_ConsistencyCheckStart;
      CommandMailbox->ConsistencyCheck.RestoreConsistency = true;
      CommandMailbox->ConsistencyCheck.InitializedAreaOnly = false;
      DAC960_ExecuteCommand(Command);
      DAC960_UserCritical("Consistency Check of Logical Drive %d "
			  "(/dev/rd/c%dd%d) %s\n",
			  Controller, LogicalDriveNumber,
			  Controller->ControllerNumber,
			  LogicalDriveNumber,
			  (Command->V2.CommandStatus
			   == DAC960_V2_NormalCompletion
			   ? "Initiated" : "Not Initiated"));
    }
  else if (strncmp(UserCommand, "cancel-consistency-check", 24) == 0 &&
	   DAC960_ParseLogicalDrive(Controller, &UserCommand[24],
				    &LogicalDriveNumber))
    {
      CommandMailbox->ConsistencyCheck.LogicalDevice.LogicalDeviceNumber =
	LogicalDriveNumber;
      CommandMailbox->ConsistencyCheck.IOCTL_Opcode =
	DAC960_V2_ConsistencyCheckStop;
      DAC960_ExecuteCommand(Command);
      DAC960_UserCritical("Consistency Check of Logical Drive %d "
			  "(/dev/rd/c%dd%d) %s\n",
			  Controller, LogicalDriveNumber,
			  Controller->ControllerNumber,
			  LogicalDriveNumber,
			  (Command->V2.CommandStatus
			   == DAC960_V2_NormalCompletion
			   ? "Cancelled" : "Not Cancelled"));
    }
  else if (strcmp(UserCommand, "perform-discovery") == 0)
    {
      CommandMailbox->Common.IOCTL_Opcode = DAC960_V2_StartDiscovery;
      DAC960_ExecuteCommand(Command);
      DAC960_UserCritical("Discovery %s\n", Controller,
			  (Command->V2.CommandStatus
			   == DAC960_V2_NormalCompletion
			   ? "Initiated" : "Not Initiated"));
      if (Command->V2.CommandStatus == DAC960_V2_NormalCompletion)
	{
	  CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL;
	  CommandMailbox->ControllerInfo.CommandControlBits
					.DataTransferControllerToHost = true;
	  CommandMailbox->ControllerInfo.CommandControlBits
					.NoAutoRequestSense = true;
	  CommandMailbox->ControllerInfo.DataTransferSize =
	    sizeof(DAC960_V2_ControllerInfo_T);
	  CommandMailbox->ControllerInfo.ControllerNumber = 0;
	  CommandMailbox->ControllerInfo.IOCTL_Opcode =
	    DAC960_V2_GetControllerInfo;
	  /*
	   * How does this NOT race with the queued Monitoring
	   * usage of this structure?
	   */
	  CommandMailbox->ControllerInfo.DataTransferMemoryAddress
					.ScatterGatherSegments[0]
					.SegmentDataPointer =
	    Controller->V2.NewControllerInformationDMA;
	  CommandMailbox->ControllerInfo.DataTransferMemoryAddress
					.ScatterGatherSegments[0]
					.SegmentByteCount =
	    CommandMailbox->ControllerInfo.DataTransferSize;
	  DAC960_ExecuteCommand(Command);
	  while (Controller->V2.NewControllerInformation->PhysicalScanActive)
	    {
	      DAC960_ExecuteCommand(Command);
	      sleep_on_timeout(&Controller->CommandWaitQueue, HZ);
	    }
	  DAC960_UserCritical("Discovery Completed\n", Controller);
 	}
    }
  else if (strcmp(UserCommand, "suppress-enclosure-messages") == 0)
    Controller->SuppressEnclosureMessages = true;
  else DAC960_UserCritical("Illegal User Command: '%s'\n",
			   Controller, UserCommand);

  spin_lock_irqsave(&Controller->queue_lock, flags);
  DAC960_DeallocateCommand(Command);
  spin_unlock_irqrestore(&Controller->queue_lock, flags);
  return true;
}


/*
  DAC960_ProcReadStatus implements reading /proc/rd/status.
*/

static int DAC960_ProcReadStatus(char *Page, char **Start, off_t Offset,
				 int Count, int *EOF, void *Data)
{
  unsigned char *StatusMessage = "OK\n";
  int ControllerNumber, BytesAvailable;
  for (ControllerNumber = 0;
       ControllerNumber < DAC960_ControllerCount;
       ControllerNumber++)
    {
      DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
      if (Controller == NULL) continue;
      if (Controller->MonitoringAlertMode)
	{
	  StatusMessage = "ALERT\n";
	  break;
	}
    }
  BytesAvailable = strlen(StatusMessage) - Offset;
  if (Count >= BytesAvailable)
    {
      Count = BytesAvailable;
      *EOF = true;
    }
  if (Count <= 0) return 0;
  *Start = Page;
  memcpy(Page, &StatusMessage[Offset], Count);
  return Count;
}


/*
  DAC960_ProcReadInitialStatus implements reading /proc/rd/cN/initial_status.
*/

static int DAC960_ProcReadInitialStatus(char *Page, char **Start, off_t Offset,
					int Count, int *EOF, void *Data)
{
  DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data;
  int BytesAvailable = Controller->InitialStatusLength - Offset;
  if (Count >= BytesAvailable)
    {
      Count = BytesAvailable;
      *EOF = true;
    }
  if (Count <= 0) return 0;
  *Start = Page;
  memcpy(Page, &Controller->CombinedStatusBuffer[Offset], Count);
  return Count;
}


/*
  DAC960_ProcReadCurrentStatus implements reading /proc/rd/cN/current_status.
*/

static int DAC960_ProcReadCurrentStatus(char *Page, char **Start, off_t Offset,
					int Count, int *EOF, void *Data)
{
  DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data;
  unsigned char *StatusMessage =
    "No Rebuild or Consistency Check in Progress\n";
  int ProgressMessageLength = strlen(StatusMessage);
  int BytesAvailable;
  if (jiffies != Controller->LastCurrentStatusTime)
    {
      Controller->CurrentStatusLength = 0;
      DAC960_AnnounceDriver(Controller);
      DAC960_ReportControllerConfiguration(Controller);
      DAC960_ReportDeviceConfiguration(Controller);
      if (Controller->ProgressBufferLength > 0)
	ProgressMessageLength = Controller->ProgressBufferLength;
      if (DAC960_CheckStatusBuffer(Controller, 2 + ProgressMessageLength))
	{
	  unsigned char *CurrentStatusBuffer = Controller->CurrentStatusBuffer;
	  CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' ';
	  CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' ';
	  if (Controller->ProgressBufferLength > 0)
	    strcpy(&CurrentStatusBuffer[Controller->CurrentStatusLength],
		   Controller->ProgressBuffer);
	  else
	    strcpy(&CurrentStatusBuffer[Controller->CurrentStatusLength],
		   StatusMessage);
	  Controller->CurrentStatusLength += ProgressMessageLength;
	}
      Controller->LastCurrentStatusTime = jiffies;
    }
  BytesAvailable = Controller->CurrentStatusLength - Offset;
  if (Count >= BytesAvailable)
    {
      Count = BytesAvailable;
      *EOF = true;
    }
  if (Count <= 0) return 0;
  *Start = Page;
  memcpy(Page, &Controller->CurrentStatusBuffer[Offset], Count);
  return Count;
}


/*
  DAC960_ProcReadUserCommand implements reading /proc/rd/cN/user_command.
*/

static int DAC960_ProcReadUserCommand(char *Page, char **Start, off_t Offset,
				      int Count, int *EOF, void *Data)
{
  DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data;
  int BytesAvailable = Controller->UserStatusLength - Offset;
  if (Count >= BytesAvailable)
    {
      Count = BytesAvailable;
      *EOF = true;
    }
  if (Count <= 0) return 0;
  *Start = Page;
  memcpy(Page, &Controller->UserStatusBuffer[Offset], Count);
  return Count;
}


/*
  DAC960_ProcWriteUserCommand implements writing /proc/rd/cN/user_command.
*/

static int DAC960_ProcWriteUserCommand(struct file *file,
				       const char __user *Buffer,
				       unsigned long Count, void *Data)
{
  DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data;
  unsigned char CommandBuffer[80];
  int Length;
  if (Count > sizeof(CommandBuffer)-1) return -EINVAL;
  if (copy_from_user(CommandBuffer, Buffer, Count)) return -EFAULT;
  CommandBuffer[Count] = '\0';
  Length = strlen(CommandBuffer);
  if (CommandBuffer[Length-1] == '\n')
    CommandBuffer[--Length] = '\0';
  if (Controller->FirmwareType == DAC960_V1_Controller)
    return (DAC960_V1_ExecuteUserCommand(Controller, CommandBuffer)
	    ? Count : -EBUSY);
  else
    return (DAC960_V2_ExecuteUserCommand(Controller, CommandBuffer)
	    ? Count : -EBUSY);
}


/*
  DAC960_CreateProcEntries creates the /proc/rd/... entries for the
  DAC960 Driver.
*/

static void DAC960_CreateProcEntries(DAC960_Controller_T *Controller)
{
	struct proc_dir_entry *StatusProcEntry;
	struct proc_dir_entry *ControllerProcEntry;
	struct proc_dir_entry *UserCommandProcEntry;

	if (DAC960_ProcDirectoryEntry == NULL) {
  		DAC960_ProcDirectoryEntry = proc_mkdir("rd", NULL);
  		StatusProcEntry = create_proc_read_entry("status", 0,
					   DAC960_ProcDirectoryEntry,
					   DAC960_ProcReadStatus, NULL);
	}

      sprintf(Controller->ControllerName, "c%d", Controller->ControllerNumber);
      ControllerProcEntry = proc_mkdir(Controller->ControllerName,
				       DAC960_ProcDirectoryEntry);
      create_proc_read_entry("initial_status", 0, ControllerProcEntry,
			     DAC960_ProcReadInitialStatus, Controller);
      create_proc_read_entry("current_status", 0, ControllerProcEntry,
			     DAC960_ProcReadCurrentStatus, Controller);
      UserCommandProcEntry =
	create_proc_read_entry("user_command", S_IWUSR | S_IRUSR,
			       ControllerProcEntry, DAC960_ProcReadUserCommand,
			       Controller);
      UserCommandProcEntry->write_proc = DAC960_ProcWriteUserCommand;
      Controller->ControllerProcEntry = ControllerProcEntry;
}


/*
  DAC960_DestroyProcEntries destroys the /proc/rd/... entries for the
  DAC960 Driver.
*/

static void DAC960_DestroyProcEntries(DAC960_Controller_T *Controller)
{
      if (Controller->ControllerProcEntry == NULL)
	      return;
      remove_proc_entry("initial_status", Controller->ControllerProcEntry);
      remove_proc_entry("current_status", Controller->ControllerProcEntry);
      remove_proc_entry("user_command", Controller->ControllerProcEntry);
      remove_proc_entry(Controller->ControllerName, DAC960_ProcDirectoryEntry);
      Controller->ControllerProcEntry = NULL;
}

#ifdef DAC960_GAM_MINOR

/*
 * DAC960_gam_ioctl is the ioctl function for performing RAID operations.
*/

static int DAC960_gam_ioctl(struct inode *inode, struct file *file,
			    unsigned int Request, unsigned long Argument)
{
  int ErrorCode = 0;
  if (!capable(CAP_SYS_ADMIN)) return -EACCES;
  switch (Request)
    {
    case DAC960_IOCTL_GET_CONTROLLER_COUNT:
      return DAC960_ControllerCount;
    case DAC960_IOCTL_GET_CONTROLLER_INFO:
      {
	DAC960_ControllerInfo_T __user *UserSpaceControllerInfo =
	  (DAC960_ControllerInfo_T __user *) Argument;
	DAC960_ControllerInfo_T ControllerInfo;
	DAC960_Controller_T *Controller;
	int ControllerNumber;
	if (UserSpaceControllerInfo == NULL) return -EINVAL;
	ErrorCode = get_user(ControllerNumber,
			     &UserSpaceControllerInfo->ControllerNumber);
	if (ErrorCode != 0) return ErrorCode;
	if (ControllerNumber < 0 ||
	    ControllerNumber > DAC960_ControllerCount - 1)
	  return -ENXIO;
	Controller = DAC960_Controllers[ControllerNumber];
	if (Controller == NULL) return -ENXIO;
	memset(&ControllerInfo, 0, sizeof(DAC960_ControllerInfo_T));
	ControllerInfo.ControllerNumber = ControllerNumber;
	ControllerInfo.FirmwareType = Controller->FirmwareType;
	ControllerInfo.Channels = Controller->Channels;
	ControllerInfo.Targets = Controller->Targets;
	ControllerInfo.PCI_Bus = Controller->Bus;
	ControllerInfo.PCI_Device = Controller->Device;
	ControllerInfo.PCI_Function = Controller->Function;
	ControllerInfo.IRQ_Channel = Controller->IRQ_Channel;
	ControllerInfo.PCI_Address = Controller->PCI_Address;
	strcpy(ControllerInfo.ModelName, Controller->ModelName);
	strcpy(ControllerInfo.FirmwareVersion, Controller->FirmwareVersion);
	return (copy_to_user(UserSpaceControllerInfo, &ControllerInfo,
			     sizeof(DAC960_ControllerInfo_T)) ? -EFAULT : 0);
      }
    case DAC960_IOCTL_V1_EXECUTE_COMMAND:
      {
	DAC960_V1_UserCommand_T __user *UserSpaceUserCommand =
	  (DAC960_V1_UserCommand_T __user *) Argument;
	DAC960_V1_UserCommand_T UserCommand;
	DAC960_Controller_T *Controller;
	DAC960_Command_T *Command = NULL;
	DAC960_V1_CommandOpcode_T CommandOpcode;
	DAC960_V1_CommandStatus_T CommandStatus;
	DAC960_V1_DCDB_T DCDB;
	DAC960_V1_DCDB_T *DCDB_IOBUF = NULL;
	dma_addr_t	DCDB_IOBUFDMA;
	unsigned long flags;
	int ControllerNumber, DataTransferLength;
	unsigned char *DataTransferBuffer = NULL;
	dma_addr_t DataTransferBufferDMA;
	if (UserSpaceUserCommand == NULL) return -EINVAL;
	if (copy_from_user(&UserCommand, UserSpaceUserCommand,
				   sizeof(DAC960_V1_UserCommand_T))) {
		ErrorCode = -EFAULT;
		goto Failure1a;
	}
	ControllerNumber = UserCommand.ControllerNumber;
	if (ControllerNumber < 0 ||
	    ControllerNumber > DAC960_ControllerCount - 1)
	  return -ENXIO;
	Controller = DAC960_Controllers[ControllerNumber];
	if (Controller == NULL) return -ENXIO;
	if (Controller->FirmwareType != DAC960_V1_Controller) return -EINVAL;
	CommandOpcode = UserCommand.CommandMailbox.Common.CommandOpcode;
	DataTransferLength = UserCommand.DataTransferLength;
	if (CommandOpcode & 0x80) return -EINVAL;
	if (CommandOpcode == DAC960_V1_DCDB)
	  {
	    if (copy_from_user(&DCDB, UserCommand.DCDB,
			       sizeof(DAC960_V1_DCDB_T))) {
		ErrorCode = -EFAULT;
		goto Failure1a;
	    }
	    if (DCDB.Channel >= DAC960_V1_MaxChannels) return -EINVAL;
	    if (!((DataTransferLength == 0 &&
		   DCDB.Direction
		   == DAC960_V1_DCDB_NoDataTransfer) ||
		  (DataTransferLength > 0 &&
		   DCDB.Direction
		   == DAC960_V1_DCDB_DataTransferDeviceToSystem) ||
		  (DataTransferLength < 0 &&
		   DCDB.Direction
		   == DAC960_V1_DCDB_DataTransferSystemToDevice)))
	      return -EINVAL;
	    if (((DCDB.TransferLengthHigh4 << 16) | DCDB.TransferLength)
		!= abs(DataTransferLength))
	      return -EINVAL;
	    DCDB_IOBUF = pci_alloc_consistent(Controller->PCIDevice,
			sizeof(DAC960_V1_DCDB_T), &DCDB_IOBUFDMA);
	    if (DCDB_IOBUF == NULL)
			return -ENOMEM;
	  }
	if (DataTransferLength > 0)
	  {
	    DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice,
				DataTransferLength, &DataTransferBufferDMA);
	    if (DataTransferBuffer == NULL) {
		ErrorCode = -ENOMEM;
		goto Failure1;
	    }
	    memset(DataTransferBuffer, 0, DataTransferLength);
	  }
	else if (DataTransferLength < 0)
	  {
	    DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice,
				-DataTransferLength, &DataTransferBufferDMA);
	    if (DataTransferBuffer == NULL) {
		ErrorCode = -ENOMEM;
		goto Failure1;
	    }
	    if (copy_from_user(DataTransferBuffer,
			       UserCommand.DataTransferBuffer,
			       -DataTransferLength)) {
		ErrorCode = -EFAULT;
		goto Failure1;
	    }
	  }
	if (CommandOpcode == DAC960_V1_DCDB)
	  {
	    spin_lock_irqsave(&Controller->queue_lock, flags);
	    while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
	      DAC960_WaitForCommand(Controller);
	    while (Controller->V1.DirectCommandActive[DCDB.Channel]
						     [DCDB.TargetID])
	      {
		spin_unlock_irq(&Controller->queue_lock);
		__wait_event(Controller->CommandWaitQueue,
			     !Controller->V1.DirectCommandActive
					     [DCDB.Channel][DCDB.TargetID]);
		spin_lock_irq(&Controller->queue_lock);
	      }
	    Controller->V1.DirectCommandActive[DCDB.Channel]
					      [DCDB.TargetID] = true;
	    spin_unlock_irqrestore(&Controller->queue_lock, flags);
	    DAC960_V1_ClearCommand(Command);
	    Command->CommandType = DAC960_ImmediateCommand;
	    memcpy(&Command->V1.CommandMailbox, &UserCommand.CommandMailbox,
		   sizeof(DAC960_V1_CommandMailbox_T));
	    Command->V1.CommandMailbox.Type3.BusAddress = DCDB_IOBUFDMA;
	    DCDB.BusAddress = DataTransferBufferDMA;
	    memcpy(DCDB_IOBUF, &DCDB, sizeof(DAC960_V1_DCDB_T));
	  }
	else
	  {
	    spin_lock_irqsave(&Controller->queue_lock, flags);
	    while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
	      DAC960_WaitForCommand(Controller);
	    spin_unlock_irqrestore(&Controller->queue_lock, flags);
	    DAC960_V1_ClearCommand(Command);
	    Command->CommandType = DAC960_ImmediateCommand;
	    memcpy(&Command->V1.CommandMailbox, &UserCommand.CommandMailbox,
		   sizeof(DAC960_V1_CommandMailbox_T));
	    if (DataTransferBuffer != NULL)
	      Command->V1.CommandMailbox.Type3.BusAddress =
		DataTransferBufferDMA;
	  }
	DAC960_ExecuteCommand(Command);
	CommandStatus = Command->V1.CommandStatus;
	spin_lock_irqsave(&Controller->queue_lock, flags);
	DAC960_DeallocateCommand(Command);
	spin_unlock_irqrestore(&Controller->queue_lock, flags);
	if (DataTransferLength > 0)
	  {
	    if (copy_to_user(UserCommand.DataTransferBuffer,
			     DataTransferBuffer, DataTransferLength)) {
		ErrorCode = -EFAULT;
		goto Failure1;
            }
	  }
	if (CommandOpcode == DAC960_V1_DCDB)
	  {
	    /*
	      I don't believe Target or Channel in the DCDB_IOBUF
	      should be any different from the contents of DCDB.
	     */
	    Controller->V1.DirectCommandActive[DCDB.Channel]
					      [DCDB.TargetID] = false;
	    if (copy_to_user(UserCommand.DCDB, DCDB_IOBUF,
			     sizeof(DAC960_V1_DCDB_T))) {
		ErrorCode = -EFAULT;
		goto Failure1;
	    }
	  }
	ErrorCode = CommandStatus;
      Failure1:
	if (DataTransferBuffer != NULL)
	  pci_free_consistent(Controller->PCIDevice, abs(DataTransferLength),
			DataTransferBuffer, DataTransferBufferDMA);
	if (DCDB_IOBUF != NULL)
	  pci_free_consistent(Controller->PCIDevice, sizeof(DAC960_V1_DCDB_T),
			DCDB_IOBUF, DCDB_IOBUFDMA);
      Failure1a:
	return ErrorCode;
      }
    case DAC960_IOCTL_V2_EXECUTE_COMMAND:
      {
	DAC960_V2_UserCommand_T __user *UserSpaceUserCommand =
	  (DAC960_V2_UserCommand_T __user *) Argument;
	DAC960_V2_UserCommand_T UserCommand;
	DAC960_Controller_T *Controller;
	DAC960_Command_T *Command = NULL;
	DAC960_V2_CommandMailbox_T *CommandMailbox;
	DAC960_V2_CommandStatus_T CommandStatus;
	unsigned long flags;
	int ControllerNumber, DataTransferLength;
	int DataTransferResidue, RequestSenseLength;
	unsigned char *DataTransferBuffer = NULL;
	dma_addr_t DataTransferBufferDMA;
	unsigned char *RequestSenseBuffer = NULL;
	dma_addr_t RequestSenseBufferDMA;
	if (UserSpaceUserCommand == NULL) return -EINVAL;
	if (copy_from_user(&UserCommand, UserSpaceUserCommand,
			   sizeof(DAC960_V2_UserCommand_T))) {
		ErrorCode = -EFAULT;
		goto Failure2a;
	}
	ControllerNumber = UserCommand.ControllerNumber;
	if (ControllerNumber < 0 ||
	    ControllerNumber > DAC960_ControllerCount - 1)
	  return -ENXIO;
	Controller = DAC960_Controllers[ControllerNumber];
	if (Controller == NULL) return -ENXIO;
	if (Controller->FirmwareType != DAC960_V2_Controller) return -EINVAL;
	DataTransferLength = UserCommand.DataTransferLength;
	if (DataTransferLength > 0)
	  {
	    DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice,
				DataTransferLength, &DataTransferBufferDMA);
	    if (DataTransferBuffer == NULL) return -ENOMEM;
	    memset(DataTransferBuffer, 0, DataTransferLength);
	  }
	else if (DataTransferLength < 0)
	  {
	    DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice,
				-DataTransferLength, &DataTransferBufferDMA);
	    if (DataTransferBuffer == NULL) return -ENOMEM;
	    if (copy_from_user(DataTransferBuffer,
			       UserCommand.DataTransferBuffer,
			       -DataTransferLength)) {
		ErrorCode = -EFAULT;
		goto Failure2;
	    }
	  }
	RequestSenseLength = UserCommand.RequestSenseLength;
	if (RequestSenseLength > 0)
	  {
	    RequestSenseBuffer = pci_alloc_consistent(Controller->PCIDevice,
			RequestSenseLength, &RequestSenseBufferDMA);
	    if (RequestSenseBuffer == NULL)
	      {
		ErrorCode = -ENOMEM;
		goto Failure2;
	      }
	    memset(RequestSenseBuffer, 0, RequestSenseLength);
	  }
	spin_lock_irqsave(&Controller->queue_lock, flags);
	while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
	  DAC960_WaitForCommand(Controller);
	spin_unlock_irqrestore(&Controller->queue_lock, flags);
	DAC960_V2_ClearCommand(Command);
	Command->CommandType = DAC960_ImmediateCommand;
	CommandMailbox = &Command->V2.CommandMailbox;
	memcpy(CommandMailbox, &UserCommand.CommandMailbox,
	       sizeof(DAC960_V2_CommandMailbox_T));
	CommandMailbox->Common.CommandControlBits
			      .AdditionalScatterGatherListMemory = false;
	CommandMailbox->Common.CommandControlBits
			      .NoAutoRequestSense = true;
	CommandMailbox->Common.DataTransferSize = 0;
	CommandMailbox->Common.DataTransferPageNumber = 0;
	memset(&CommandMailbox->Common.DataTransferMemoryAddress, 0,
	       sizeof(DAC960_V2_DataTransferMemoryAddress_T));
	if (DataTransferLength != 0)
	  {
	    if (DataTransferLength > 0)
	      {
		CommandMailbox->Common.CommandControlBits
				      .DataTransferControllerToHost = true;
		CommandMailbox->Common.DataTransferSize = DataTransferLength;
	      }
	    else
	      {
		CommandMailbox->Common.CommandControlBits
				      .DataTransferControllerToHost = false;
		CommandMailbox->Common.DataTransferSize = -DataTransferLength;
	      }
	    CommandMailbox->Common.DataTransferMemoryAddress
				  .ScatterGatherSegments[0]
				  .SegmentDataPointer = DataTransferBufferDMA;
	    CommandMailbox->Common.DataTransferMemoryAddress
				  .ScatterGatherSegments[0]
				  .SegmentByteCount =
	      CommandMailbox->Common.DataTransferSize;
	  }
	if (RequestSenseLength > 0)
	  {
	    CommandMailbox->Common.CommandControlBits
				  .NoAutoRequestSense = false;
	    CommandMailbox->Common.RequestSenseSize = RequestSenseLength;
	    CommandMailbox->Common.RequestSenseBusAddress =
	      						RequestSenseBufferDMA;
	  }
	DAC960_ExecuteCommand(Command);
	CommandStatus = Command->V2.CommandStatus;
	RequestSenseLength = Command->V2.RequestSenseLength;
	DataTransferResidue = Command->V2.DataTransferResidue;
	spin_lock_irqsave(&Controller->queue_lock, flags);
	DAC960_DeallocateCommand(Command);
	spin_unlock_irqrestore(&Controller->queue_lock, flags);
	if (RequestSenseLength > UserCommand.RequestSenseLength)
	  RequestSenseLength = UserCommand.RequestSenseLength;
	if (copy_to_user(&UserSpaceUserCommand->DataTransferLength,
				 &DataTransferResidue,
				 sizeof(DataTransferResidue))) {
		ErrorCode = -EFAULT;
		goto Failure2;
	}
	if (copy_to_user(&UserSpaceUserCommand->RequestSenseLength,
			 &RequestSenseLength, sizeof(RequestSenseLength))) {
		ErrorCode = -EFAULT;
		goto Failure2;
	}
	if (DataTransferLength > 0)
	  {
	    if (copy_to_user(UserCommand.DataTransferBuffer,
			     DataTransferBuffer, DataTransferLength)) {
		ErrorCode = -EFAULT;
		goto Failure2;
	    }
	  }
	if (RequestSenseLength > 0)
	  {
	    if (copy_to_user(UserCommand.RequestSenseBuffer,
			     RequestSenseBuffer, RequestSenseLength)) {
		ErrorCode = -EFAULT;
		goto Failure2;
	    }
	  }
	ErrorCode = CommandStatus;
      Failure2:
	  pci_free_consistent(Controller->PCIDevice, abs(DataTransferLength),
		DataTransferBuffer, DataTransferBufferDMA);
	if (RequestSenseBuffer != NULL)
	  pci_free_consistent(Controller->PCIDevice, RequestSenseLength,
		RequestSenseBuffer, RequestSenseBufferDMA);
      Failure2a:
	return ErrorCode;
      }
    case DAC960_IOCTL_V2_GET_HEALTH_STATUS:
      {
	DAC960_V2_GetHealthStatus_T __user *UserSpaceGetHealthStatus =
	  (DAC960_V2_GetHealthStatus_T __user *) Argument;
	DAC960_V2_GetHealthStatus_T GetHealthStatus;
	DAC960_V2_HealthStatusBuffer_T HealthStatusBuffer;
	DAC960_Controller_T *Controller;
	int ControllerNumber;
	if (UserSpaceGetHealthStatus == NULL) return -EINVAL;
	if (copy_from_user(&GetHealthStatus, UserSpaceGetHealthStatus,
			   sizeof(DAC960_V2_GetHealthStatus_T)))
		return -EFAULT;
	ControllerNumber = GetHealthStatus.ControllerNumber;
	if (ControllerNumber < 0 ||
	    ControllerNumber > DAC960_ControllerCount - 1)
	  return -ENXIO;
	Controller = DAC960_Controllers[ControllerNumber];
	if (Controller == NULL) return -ENXIO;
	if (Controller->FirmwareType != DAC960_V2_Controller) return -EINVAL;
	if (copy_from_user(&HealthStatusBuffer,
			   GetHealthStatus.HealthStatusBuffer,
			   sizeof(DAC960_V2_HealthStatusBuffer_T)))
		return -EFAULT;
	while (Controller->V2.HealthStatusBuffer->StatusChangeCounter
	       == HealthStatusBuffer.StatusChangeCounter &&
	       Controller->V2.HealthStatusBuffer->NextEventSequenceNumber
	       == HealthStatusBuffer.NextEventSequenceNumber)
	  {
	    interruptible_sleep_on_timeout(&Controller->HealthStatusWaitQueue,
					   DAC960_MonitoringTimerInterval);
	    if (signal_pending(current)) return -EINTR;
	  }
	if (copy_to_user(GetHealthStatus.HealthStatusBuffer,
			 Controller->V2.HealthStatusBuffer,
			 sizeof(DAC960_V2_HealthStatusBuffer_T)))
		return -EFAULT;
	return 0;
      }
    }
  return -EINVAL;
}

static const struct file_operations DAC960_gam_fops = {
	.owner		= THIS_MODULE,
	.ioctl		= DAC960_gam_ioctl
};

static struct miscdevice DAC960_gam_dev = {
	DAC960_GAM_MINOR,
	"dac960_gam",
	&DAC960_gam_fops
};

static int DAC960_gam_init(void)
{
	int ret;

	ret = misc_register(&DAC960_gam_dev);
	if (ret)
		printk(KERN_ERR "DAC960_gam: can't misc_register on minor %d\n", DAC960_GAM_MINOR);
	return ret;
}

static void DAC960_gam_cleanup(void)
{
	misc_deregister(&DAC960_gam_dev);
}

#endif /* DAC960_GAM_MINOR */

static struct DAC960_privdata DAC960_GEM_privdata = {
	.HardwareType =		DAC960_GEM_Controller,
	.FirmwareType 	=	DAC960_V2_Controller,
	.InterruptHandler =	DAC960_GEM_InterruptHandler,
	.MemoryWindowSize =	DAC960_GEM_RegisterWindowSize,
};


static struct DAC960_privdata DAC960_BA_privdata = {
	.HardwareType =		DAC960_BA_Controller,
	.FirmwareType 	=	DAC960_V2_Controller,
	.InterruptHandler =	DAC960_BA_InterruptHandler,
	.MemoryWindowSize =	DAC960_BA_RegisterWindowSize,
};

static struct DAC960_privdata DAC960_LP_privdata = {
	.HardwareType =		DAC960_LP_Controller,
	.FirmwareType 	=	DAC960_LP_Controller,
	.InterruptHandler =	DAC960_LP_InterruptHandler,
	.MemoryWindowSize =	DAC960_LP_RegisterWindowSize,
};

static struct DAC960_privdata DAC960_LA_privdata = {
	.HardwareType =		DAC960_LA_Controller,
	.FirmwareType 	=	DAC960_V1_Controller,
	.InterruptHandler =	DAC960_LA_InterruptHandler,
	.MemoryWindowSize =	DAC960_LA_RegisterWindowSize,
};

static struct DAC960_privdata DAC960_PG_privdata = {
	.HardwareType =		DAC960_PG_Controller,
	.FirmwareType 	=	DAC960_V1_Controller,
	.InterruptHandler =	DAC960_PG_InterruptHandler,
	.MemoryWindowSize =	DAC960_PG_RegisterWindowSize,
};

static struct DAC960_privdata DAC960_PD_privdata = {
	.HardwareType =		DAC960_PD_Controller,
	.FirmwareType 	=	DAC960_V1_Controller,
	.InterruptHandler =	DAC960_PD_InterruptHandler,
	.MemoryWindowSize =	DAC960_PD_RegisterWindowSize,
};

static struct DAC960_privdata DAC960_P_privdata = {
	.HardwareType =		DAC960_P_Controller,
	.FirmwareType 	=	DAC960_V1_Controller,
	.InterruptHandler =	DAC960_P_InterruptHandler,
	.MemoryWindowSize =	DAC960_PD_RegisterWindowSize,
};

static struct pci_device_id DAC960_id_table[] = {
	{
		.vendor 	= PCI_VENDOR_ID_MYLEX,
		.device		= PCI_DEVICE_ID_MYLEX_DAC960_GEM,
		.subvendor	= PCI_VENDOR_ID_MYLEX,
		.subdevice	= PCI_ANY_ID,
		.driver_data	= (unsigned long) &DAC960_GEM_privdata,
	},
	{
		.vendor 	= PCI_VENDOR_ID_MYLEX,
		.device		= PCI_DEVICE_ID_MYLEX_DAC960_BA,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.driver_data	= (unsigned long) &DAC960_BA_privdata,
	},
	{
		.vendor 	= PCI_VENDOR_ID_MYLEX,
		.device		= PCI_DEVICE_ID_MYLEX_DAC960_LP,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.driver_data	= (unsigned long) &DAC960_LP_privdata,
	},
	{
		.vendor 	= PCI_VENDOR_ID_DEC,
		.device		= PCI_DEVICE_ID_DEC_21285,
		.subvendor	= PCI_VENDOR_ID_MYLEX,
		.subdevice	= PCI_DEVICE_ID_MYLEX_DAC960_LA,
		.driver_data	= (unsigned long) &DAC960_LA_privdata,
	},
	{
		.vendor 	= PCI_VENDOR_ID_MYLEX,
		.device		= PCI_DEVICE_ID_MYLEX_DAC960_PG,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.driver_data	= (unsigned long) &DAC960_PG_privdata,
	},
	{
		.vendor 	= PCI_VENDOR_ID_MYLEX,
		.device		= PCI_DEVICE_ID_MYLEX_DAC960_PD,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.driver_data	= (unsigned long) &DAC960_PD_privdata,
	},
	{
		.vendor 	= PCI_VENDOR_ID_MYLEX,
		.device		= PCI_DEVICE_ID_MYLEX_DAC960_P,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.driver_data	= (unsigned long) &DAC960_P_privdata,
	},
	{0, },
};

MODULE_DEVICE_TABLE(pci, DAC960_id_table);

static struct pci_driver DAC960_pci_driver = {
	.name		= "DAC960",
	.id_table	= DAC960_id_table,
	.probe		= DAC960_Probe,
	.remove		= DAC960_Remove,
};

static int DAC960_init_module(void)
{
	int ret;

	ret =  pci_register_driver(&DAC960_pci_driver);
#ifdef DAC960_GAM_MINOR
	if (!ret)
		DAC960_gam_init();
#endif
	return ret;
}

static void DAC960_cleanup_module(void)
{
	int i;

#ifdef DAC960_GAM_MINOR
	DAC960_gam_cleanup();
#endif

	for (i = 0; i < DAC960_ControllerCount; i++) {
		DAC960_Controller_T *Controller = DAC960_Controllers[i];
		if (Controller == NULL)
			continue;
		DAC960_FinalizeController(Controller);
	}
	if (DAC960_ProcDirectoryEntry != NULL) {
  		remove_proc_entry("rd/status", NULL);
  		remove_proc_entry("rd", NULL);
	}
	DAC960_ControllerCount = 0;
	pci_unregister_driver(&DAC960_pci_driver);
}

module_init(DAC960_init_module);
module_exit(DAC960_cleanup_module);

MODULE_LICENSE("GPL");