Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / s390 / crypto / z90main.c
index 8ac1e9b..982acc7 100644 (file)
@@ -1,11 +1,11 @@
 /*
- *  linux/drivers/s390/misc/z90main.c
+ *  linux/drivers/s390/crypto/z90main.c
  *
- *  z90crypt 1.3.1
+ *  z90crypt 1.3.3
  *
- *  Copyright (C)  2001, 2004 IBM Corporation
+ *  Copyright (C)  2001, 2005 IBM Corporation
  *  Author(s): Robert Burroughs (burrough@us.ibm.com)
- *            Eric Rossman (edrossma@us.ibm.com)
+ *             Eric Rossman (edrossma@us.ibm.com)
  *
  *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
  *
@@ -16,7 +16,7 @@
  *
  * 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
 #include <linux/delay.h>       // mdelay
 #include <linux/init.h>
 #include <linux/interrupt.h>   // for tasklets
-#include <linux/ioctl32.h>
+#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/proc_fs.h>
 #include <linux/syscalls.h>
-#include <linux/version.h>
 #include "z90crypt.h"
 #include "z90common.h"
-#ifndef Z90CRYPT_USE_HOTPLUG
-#include <linux/miscdevice.h>
-#endif
-
-#define VERSION_CODE(vers, rel, seq) (((vers)<<16) | ((rel)<<8) | (seq))
-#if LINUX_VERSION_CODE < VERSION_CODE(2,4,0) /* version < 2.4 */
-#  error "This kernel is too old: not supported"
-#endif
-#if LINUX_VERSION_CODE > VERSION_CODE(2,7,0) /* version > 2.6 */
-#  error "This kernel is too recent: not supported by this file"
-#endif
-
-#define VERSION_Z90MAIN_C "$Revision: 1.31 $"
-
-static char z90cmain_version[] __initdata =
-       "z90main.o (" VERSION_Z90MAIN_C "/"
-                      VERSION_Z90COMMON_H "/" VERSION_Z90CRYPT_H ")";
-
-extern char z90chardware_version[];
 
 /**
  * Defaults that may be modified.
  */
 
-#ifndef Z90CRYPT_USE_HOTPLUG
 /**
  * You can specify a different minor at compile time.
  */
 #ifndef Z90CRYPT_MINOR
 #define Z90CRYPT_MINOR MISC_DYNAMIC_MINOR
 #endif
-#else
-/**
- * You can specify a different major at compile time.
- */
-#ifndef Z90CRYPT_MAJOR
-#define Z90CRYPT_MAJOR 0
-#endif
-#endif
 
 /**
  * You can specify a different domain at compile time or on the insmod
@@ -116,9 +87,15 @@ extern char z90chardware_version[];
 
 /**
  * Reader should run every READERTIME milliseconds
+ * With the 100Hz patch for s390, z90crypt can lock the system solid while
+ * under heavy load. We'll try to avoid that.
  */
 #ifndef READERTIME
+#if HZ > 1000
 #define READERTIME 2
+#else
+#define READERTIME 10
+#endif
 #endif
 
 /**
@@ -209,18 +186,13 @@ extern char z90chardware_version[];
 #ifndef Z90CRYPT_NUM_DEVS
 #define Z90CRYPT_NUM_DEVS Z90CRYPT_NUM_APS
 #endif
-#ifndef Z90CRYPT_NUM_TYPES
-#define Z90CRYPT_NUM_TYPES 3
-#endif
 
 /**
  * Buffer size for receiving responses. The maximum Response Size
  * is actually the maximum request size, since in an error condition
  * the request itself may be returned unchanged.
  */
-#ifndef MAX_RESPONSE_SIZE
 #define MAX_RESPONSE_SIZE 0x0000077C
-#endif
 
 /**
  * A count and status-byte mask
@@ -246,7 +218,8 @@ struct device_x {
  * All devices are arranged in a single array: 64 APs
  */
 struct device {
-       int              dev_type;          // PCICA, PCICC, or PCIXCC
+       int              dev_type;          // PCICA, PCICC, PCIXCC_MCL2,
+                                           // PCIXCC_MCL3, CEX2C, CEX2A
        enum devstat     dev_stat;          // current device status
        int              dev_self_x;        // Index in array
        int              disabled;          // Set when device is in error
@@ -295,6 +268,10 @@ struct z90crypt {
  * it contains the request; at READ, the response. The function
  * send_to_crypto_device converts the request to device-dependent
  * form and use the caller's OPEN-allocated buffer for the response.
+ *
+ * For the contents of caller_dev_dep_req and caller_dev_dep_req_p
+ * because that points to it, see the discussion in z90hardware.c.
+ * Search for "extended request message block".
  */
 struct caller {
        int              caller_buf_l;           // length of original request
@@ -309,25 +286,30 @@ struct caller {
 /**
  * Function prototypes from z90hardware.c
  */
-enum hdstat query_online(int, int, int, int *, int *);
-enum devstat reset_device(int, int, int);
-enum devstat send_to_AP(int, int, int, unsigned char *);
-enum devstat receive_from_AP(int, int, int, unsigned char *, unsigned char *);
-int convert_request(unsigned char *, int, short, int, int, int *,
-                   unsigned char *);
-int convert_response(unsigned char *, unsigned char *, int *, unsigned char *);
+enum hdstat query_online(int deviceNr, int cdx, int resetNr, int *q_depth,
+                        int *dev_type);
+enum devstat reset_device(int deviceNr, int cdx, int resetNr);
+enum devstat send_to_AP(int dev_nr, int cdx, int msg_len, unsigned char *msg_ext);
+enum devstat receive_from_AP(int dev_nr, int cdx, int resplen,
+                            unsigned char *resp, unsigned char *psmid);
+int convert_request(unsigned char *buffer, int func, unsigned short function,
+                   int cdx, int dev_type, int *msg_l_p, unsigned char *msg_p);
+int convert_response(unsigned char *response, unsigned char *buffer,
+                    int *respbufflen_p, unsigned char *resp_buff);
 
 /**
  * Low level function prototypes
  */
-static int create_z90crypt(int *);
-static int refresh_z90crypt(int *);
-static int find_crypto_devices(struct status *);
-static int create_crypto_device(int);
-static int destroy_crypto_device(int);
+static int create_z90crypt(int *cdx_p);
+static int refresh_z90crypt(int *cdx_p);
+static int find_crypto_devices(struct status *deviceMask);
+static int create_crypto_device(int index);
+static int destroy_crypto_device(int index);
 static void destroy_z90crypt(void);
-static int refresh_index_array(struct status *, struct device_x *);
-static int probe_device_type(struct device *);
+static int refresh_index_array(struct status *status_str,
+                              struct device_x *index_array);
+static int probe_device_type(struct device *devPtr);
+static int probe_PCIXCC_type(struct device *devPtr);
 
 /**
  * proc fs definitions
@@ -381,8 +363,8 @@ static int z90crypt_release(struct inode *, struct file *);
 static ssize_t z90crypt_read(struct file *, char __user *, size_t, loff_t *);
 static ssize_t z90crypt_write(struct file *, const char __user *,
                                                        size_t, loff_t *);
-static int z90crypt_ioctl(struct inode *, struct file *,
-                         unsigned int, unsigned long);
+static long z90crypt_unlocked_ioctl(struct file *, unsigned int, unsigned long);
+static long z90crypt_compat_ioctl(struct file *, unsigned int, unsigned long);
 
 static void z90crypt_reader_task(unsigned long);
 static void z90crypt_schedule_reader_task(unsigned long);
@@ -393,25 +375,10 @@ static int z90crypt_status(char *, char **, off_t, int, int *, void *);
 static int z90crypt_status_write(struct file *, const char __user *,
                                 unsigned long, void *);
 
-/**
- * Hotplug support
- */
-
-#ifdef Z90CRYPT_USE_HOTPLUG
-#define Z90CRYPT_HOTPLUG_ADD    1
-#define Z90CRYPT_HOTPLUG_REMOVE         2
-
-static void z90crypt_hotplug_event(int, int, int);
-#endif
-
 /**
  * Storage allocated at initialization and used throughout the life of
  * this insmod
  */
-#ifdef Z90CRYPT_USE_HOTPLUG
-static int z90crypt_major = Z90CRYPT_MAJOR;
-#endif
-
 static int domain = DOMAIN_INDEX;
 static struct z90crypt z90crypt;
 static int quiesce_z90crypt;
@@ -429,30 +396,31 @@ static atomic_t total_open;
 static atomic_t z90crypt_step;
 
 static struct file_operations z90crypt_fops = {
-       .owner   = THIS_MODULE,
-       .read    = z90crypt_read,
-       .write   = z90crypt_write,
-       .ioctl   = z90crypt_ioctl,
-       .open    = z90crypt_open,
-       .release = z90crypt_release
+       .owner          = THIS_MODULE,
+       .read           = z90crypt_read,
+       .write          = z90crypt_write,
+       .unlocked_ioctl = z90crypt_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = z90crypt_compat_ioctl,
+#endif
+       .open           = z90crypt_open,
+       .release        = z90crypt_release
 };
 
-#ifndef Z90CRYPT_USE_HOTPLUG
 static struct miscdevice z90crypt_misc_device = {
        .minor      = Z90CRYPT_MINOR,
        .name       = DEV_NAME,
        .fops       = &z90crypt_fops,
        .devfs_name = DEV_NAME
 };
-#endif
 
 /**
  * Documentation values.
  */
-MODULE_AUTHOR("zLinux Crypto Team: Robert H. Burroughs, Eric D. Rossman"
+MODULE_AUTHOR("zSeries Linux Crypto Team: Robert H. Burroughs, Eric D. Rossman"
              "and Jochen Roehrig");
-MODULE_DESCRIPTION("zLinux Cryptographic Coprocessor device driver, "
-                  "Copyright 2001, 2004 IBM Corporation");
+MODULE_DESCRIPTION("zSeries Linux Cryptographic Coprocessor device driver, "
+                  "Copyright 2001, 2005 IBM Corporation");
 MODULE_LICENSE("GPL");
 module_param(domain, int, 0);
 MODULE_PARM_DESC(domain, "domain index for device");
@@ -470,14 +438,13 @@ struct ica_rsa_modexpo_32 { // For 32-bit callers
        compat_uptr_t   n_modulus;
 };
 
-static int
-trans_modexpo32(unsigned int fd, unsigned int cmd, unsigned long arg,
-               struct file *file)
+static long
+trans_modexpo32(struct file *filp, unsigned int cmd, unsigned long arg)
 {
        struct ica_rsa_modexpo_32 __user *mex32u = compat_ptr(arg);
        struct ica_rsa_modexpo_32  mex32k;
        struct ica_rsa_modexpo __user *mex64;
-       int ret = 0;
+       long ret = 0;
        unsigned int i;
 
        if (!access_ok(VERIFY_WRITE, mex32u, sizeof(struct ica_rsa_modexpo_32)))
@@ -494,7 +461,7 @@ trans_modexpo32(unsigned int fd, unsigned int cmd, unsigned long arg,
            __put_user(compat_ptr(mex32k.b_key), &mex64->b_key)           ||
            __put_user(compat_ptr(mex32k.n_modulus), &mex64->n_modulus))
                return -EFAULT;
-       ret = sys_ioctl(fd, cmd, (unsigned long)mex64);
+       ret = z90crypt_unlocked_ioctl(filp, cmd, (unsigned long)mex64);
        if (!ret)
                if (__get_user(i, &mex64->outputdatalength) ||
                    __put_user(i, &mex32u->outputdatalength))
@@ -514,14 +481,13 @@ struct ica_rsa_modexpo_crt_32 { // For 32-bit callers
        compat_uptr_t   u_mult_inv;
 };
 
-static int
-trans_modexpo_crt32(unsigned int fd, unsigned int cmd, unsigned long arg,
-                   struct file *file)
+static long
+trans_modexpo_crt32(struct file *filp, unsigned int cmd, unsigned long arg)
 {
        struct ica_rsa_modexpo_crt_32 __user *crt32u = compat_ptr(arg);
        struct ica_rsa_modexpo_crt_32  crt32k;
        struct ica_rsa_modexpo_crt __user *crt64;
-       int ret = 0;
+       long ret = 0;
        unsigned int i;
 
        if (!access_ok(VERIFY_WRITE, crt32u,
@@ -542,9 +508,8 @@ trans_modexpo_crt32(unsigned int fd, unsigned int cmd, unsigned long arg,
            __put_user(compat_ptr(crt32k.np_prime), &crt64->np_prime)     ||
            __put_user(compat_ptr(crt32k.nq_prime), &crt64->nq_prime)     ||
            __put_user(compat_ptr(crt32k.u_mult_inv), &crt64->u_mult_inv))
-               ret = -EFAULT;
-       if (!ret)
-               ret = sys_ioctl(fd, cmd, (unsigned long)crt64);
+               return -EFAULT;
+       ret = z90crypt_unlocked_ioctl(filp, cmd, (unsigned long)crt64);
        if (!ret)
                if (__get_user(i, &crt64->outputdatalength) ||
                    __put_user(i, &crt32u->outputdatalength))
@@ -552,52 +517,34 @@ trans_modexpo_crt32(unsigned int fd, unsigned int cmd, unsigned long arg,
        return ret;
 }
 
-static int compatible_ioctls[] = {
-       ICAZ90STATUS, Z90QUIESCE, Z90STAT_TOTALCOUNT, Z90STAT_PCICACOUNT,
-       Z90STAT_PCICCCOUNT, Z90STAT_PCIXCCCOUNT, Z90STAT_REQUESTQ_COUNT,
-       Z90STAT_PENDINGQ_COUNT, Z90STAT_TOTALOPEN_COUNT, Z90STAT_DOMAIN_INDEX,
-       Z90STAT_STATUS_MASK, Z90STAT_QDEPTH_MASK, Z90STAT_PERDEV_REQCNT,
-};
-
-static void z90_unregister_ioctl32s(void)
-{
-       int i;
-
-       unregister_ioctl32_conversion(ICARSAMODEXPO);
-       unregister_ioctl32_conversion(ICARSACRT);
-
-       for(i = 0; i < ARRAY_SIZE(compatible_ioctls); i++)
-               unregister_ioctl32_conversion(compatible_ioctls[i]);
-}
-
-static int z90_register_ioctl32s(void)
-{
-       int result, i;
-
-       result = register_ioctl32_conversion(ICARSAMODEXPO, trans_modexpo32);
-       if (result)
-               return result;
-       result = register_ioctl32_conversion(ICARSACRT, trans_modexpo_crt32);
-       if (result)
-               return result;
-
-       for(i = 0; i < ARRAY_SIZE(compatible_ioctls); i++) {
-               result = register_ioctl32_conversion(compatible_ioctls[i],NULL);
-               if (result) {
-                       z90_unregister_ioctl32s();
-                       return result;
-               }
-       }
-       return result;
-}
-#else // !CONFIG_COMPAT
-static inline void z90_unregister_ioctl32s(void)
-{
-}
-
-static inline int z90_register_ioctl32s(void)
+static long
+z90crypt_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
-       return 0;
+       switch (cmd) {
+       case ICAZ90STATUS:
+       case Z90QUIESCE:
+       case Z90STAT_TOTALCOUNT:
+       case Z90STAT_PCICACOUNT:
+       case Z90STAT_PCICCCOUNT:
+       case Z90STAT_PCIXCCCOUNT:
+       case Z90STAT_PCIXCCMCL2COUNT:
+       case Z90STAT_PCIXCCMCL3COUNT:
+       case Z90STAT_CEX2CCOUNT:
+       case Z90STAT_REQUESTQ_COUNT:
+       case Z90STAT_PENDINGQ_COUNT:
+       case Z90STAT_TOTALOPEN_COUNT:
+       case Z90STAT_DOMAIN_INDEX:
+       case Z90STAT_STATUS_MASK:
+       case Z90STAT_QDEPTH_MASK:
+       case Z90STAT_PERDEV_REQCNT:
+               return z90crypt_unlocked_ioctl(filp, cmd, arg);
+       case ICARSAMODEXPO:
+               return trans_modexpo32(filp, cmd, arg);
+       case ICARSACRT:
+               return trans_modexpo_crt32(filp, cmd, arg);
+       default:
+               return -ENOIOCTLCMD;
+       }
 }
 #endif
 
@@ -612,26 +559,18 @@ z90crypt_init_module(void)
 
        PDEBUG("PID %d\n", PID());
 
-#ifndef Z90CRYPT_USE_HOTPLUG
+       if ((domain < -1) || (domain > 15)) {
+               PRINTKW("Invalid param: domain = %d.  Not loading.\n", domain);
+               return -EINVAL;
+       }
+
        /* Register as misc device with given minor (or get a dynamic one). */
        result = misc_register(&z90crypt_misc_device);
-       if (result <0) {
+       if (result < 0) {
                PRINTKW(KERN_ERR "misc_register (minor %d) failed with %d\n",
                        z90crypt_misc_device.minor, result);
                return result;
        }
-#else
-       /* Register the major (or get a dynamic one). */
-       result = register_chrdev(z90crypt_major, REG_NAME, &z90crypt_fops);
-       if (result < 0) {
-               PRINTKW("register_chrdev (major %d) failed with %d.\n",
-                       z90crypt_major, result);
-               return result;
-       }
-
-       if (z90crypt_major == 0)
-               z90crypt_major = result;
-#endif
 
        PDEBUG("Registered " DEV_NAME " with result %d\n", result);
 
@@ -647,18 +586,11 @@ z90crypt_init_module(void)
                PRINTKN("Version %d.%d.%d loaded, built on %s %s\n",
                        z90crypt_VERSION, z90crypt_RELEASE, z90crypt_VARIANT,
                        __DATE__, __TIME__);
-               PRINTKN("%s\n", z90cmain_version);
-               PRINTKN("%s\n", z90chardware_version);
                PDEBUG("create_z90crypt (domain index %d) successful.\n",
                       domain);
        } else
                PRINTK("No devices at startup\n");
 
-#ifdef Z90CRYPT_USE_HOTPLUG
-       /* generate hotplug event for device node generation */
-       z90crypt_hotplug_event(z90crypt_major, 0, Z90CRYPT_HOTPLUG_ADD);
-#endif
-
        /* Initialize globals. */
        spin_lock_init(&queuespinlock);
 
@@ -707,25 +639,13 @@ z90crypt_init_module(void)
        reader_timer.expires = jiffies + (READERTIME * HZ / 1000);
        add_timer(&reader_timer);
 
-       if ((result = z90_register_ioctl32s()))
-               goto init_module_cleanup;
-
        return 0; // success
 
 init_module_cleanup:
-       z90_unregister_ioctl32s();
-
-#ifndef Z90CRYPT_USE_HOTPLUG
        if ((nresult = misc_deregister(&z90crypt_misc_device)))
                PRINTK("misc_deregister failed with %d.\n", nresult);
        else
                PDEBUG("misc_deregister successful.\n");
-#else
-       if ((nresult = unregister_chrdev(z90crypt_major, REG_NAME)))
-               PRINTK("unregister_chrdev failed with %d.\n", nresult);
-       else
-               PDEBUG("unregister_chrdev successful.\n");
-#endif
 
        return result; // failure
 }
@@ -740,23 +660,12 @@ z90crypt_cleanup_module(void)
 
        PDEBUG("PID %d\n", PID());
 
-       z90_unregister_ioctl32s();
-
        remove_proc_entry("driver/z90crypt", 0);
 
-#ifndef Z90CRYPT_USE_HOTPLUG
        if ((nresult = misc_deregister(&z90crypt_misc_device)))
                PRINTK("misc_deregister failed with %d.\n", nresult);
        else
                PDEBUG("misc_deregister successful.\n");
-#else
-       z90crypt_hotplug_event(z90crypt_major, 0, Z90CRYPT_HOTPLUG_REMOVE);
-
-       if ((nresult = unregister_chrdev(z90crypt_major, REG_NAME)))
-               PRINTK("unregister_chrdev failed with %d.\n", nresult);
-       else
-               PDEBUG("unregister_chrdev successful.\n");
-#endif
 
        /* Remove the tasks */
        tasklet_kill(&reader_tasklet);
@@ -777,13 +686,11 @@ z90crypt_cleanup_module(void)
  *     z90crypt_release
  *     z90crypt_read
  *     z90crypt_write
- *     z90crypt_ioctl
+ *     z90crypt_unlocked_ioctl
  *     z90crypt_status
  *     z90crypt_status_write
  *      disable_card
  *      enable_card
- *      scan_char
- *      scan_string
  *
  * Helper functions:
  *     z90crypt_rsa
@@ -800,13 +707,12 @@ z90crypt_open(struct inode *inode, struct file *filp)
        if (quiesce_z90crypt)
                return -EQUIESCE;
 
-       private_data_p = kmalloc(sizeof(struct priv_data), GFP_KERNEL);
+       private_data_p = kzalloc(sizeof(struct priv_data), GFP_KERNEL);
        if (!private_data_p) {
                PRINTK("Memory allocate failed\n");
                return -ENOMEM;
        }
 
-       memset((void *)private_data_p, 0, sizeof(struct priv_data));
        private_data_p->status = STAT_OPEN;
        private_data_p->opener_pid = PID();
        filp->private_data = private_data_p;
@@ -923,7 +829,32 @@ get_status_PCICCcount(void)
 static inline int
 get_status_PCIXCCcount(void)
 {
-       return z90crypt.hdware_info->type_mask[PCIXCC].st_count;
+       return z90crypt.hdware_info->type_mask[PCIXCC_MCL2].st_count +
+              z90crypt.hdware_info->type_mask[PCIXCC_MCL3].st_count;
+}
+
+static inline int
+get_status_PCIXCCMCL2count(void)
+{
+       return z90crypt.hdware_info->type_mask[PCIXCC_MCL2].st_count;
+}
+
+static inline int
+get_status_PCIXCCMCL3count(void)
+{
+       return z90crypt.hdware_info->type_mask[PCIXCC_MCL3].st_count;
+}
+
+static inline int
+get_status_CEX2Ccount(void)
+{
+       return z90crypt.hdware_info->type_mask[CEX2C].st_count;
+}
+
+static inline int
+get_status_CEX2Acount(void)
+{
+       return z90crypt.hdware_info->type_mask[CEX2A].st_count;
 }
 
 static inline int
@@ -1016,8 +947,8 @@ init_work_element(struct work_element *we_p,
        we_p->audit[2] = 0x00;
        we_p->resp_buff_size = 0;
        we_p->retcode = 0;
-       we_p->devindex = -1; // send_to_crypto selects the device
-       we_p->devtype = -1;  // getCryptoBuffer selects the type
+       we_p->devindex = -1;
+       we_p->devtype = -1;
        atomic_set(&we_p->alarmrung, 0);
        init_waitqueue_head(&we_p->waitq);
        INIT_LIST_HEAD(&(we_p->liste));
@@ -1040,42 +971,132 @@ allocate_work_element(struct work_element **we_pp,
 static inline void
 remove_device(struct device *device_p)
 {
-       if (!device_p || device_p->disabled != 0)
+       if (!device_p || (device_p->disabled != 0))
                return;
        device_p->disabled = 1;
        z90crypt.hdware_info->type_mask[device_p->dev_type].disabled_count++;
        z90crypt.hdware_info->hdware_mask.disabled_count++;
 }
 
+/**
+ * Bitlength limits for each card
+ *
+ * There are new MCLs which allow more bitlengths. See the table for details.
+ * The MCL must be applied and the newer bitlengths enabled for these to work.
+ *
+ * Card Type    Old limit    New limit
+ * PCICA          ??-2048     same (the lower limit is less than 128 bit...)
+ * PCICC         512-1024     512-2048
+ * PCIXCC_MCL2   512-2048     ----- (applying any GA LIC will make an MCL3 card)
+ * PCIXCC_MCL3   -----        128-2048
+ * CEX2C         512-2048     128-2048
+ * CEX2A          ??-2048     same (the lower limit is less than 128 bit...)
+ *
+ * ext_bitlens (extended bitlengths) is a global, since you should not apply an
+ * MCL to just one card in a machine. We assume, at first, that all cards have
+ * these capabilities.
+ */
+int ext_bitlens = 1; // This is global
+#define PCIXCC_MIN_MOD_SIZE     16     //  128 bits
+#define OLD_PCIXCC_MIN_MOD_SIZE         64     //  512 bits
+#define PCICC_MIN_MOD_SIZE      64     //  512 bits
+#define OLD_PCICC_MAX_MOD_SIZE 128     // 1024 bits
+#define MAX_MOD_SIZE           256     // 2048 bits
+
 static inline int
-select_device_type(int *dev_type_p)
+select_device_type(int *dev_type_p, int bytelength)
 {
+       static int count = 0;
+       int PCICA_avail, PCIXCC_MCL3_avail, CEX2C_avail, CEX2A_avail,
+           index_to_use;
        struct status *stat;
        if ((*dev_type_p != PCICC) && (*dev_type_p != PCICA) &&
-           (*dev_type_p != PCIXCC) && (*dev_type_p != ANYDEV))
+           (*dev_type_p != PCIXCC_MCL2) && (*dev_type_p != PCIXCC_MCL3) &&
+           (*dev_type_p != CEX2C) && (*dev_type_p != CEX2A) &&
+           (*dev_type_p != ANYDEV))
                return -1;
        if (*dev_type_p != ANYDEV) {
                stat = &z90crypt.hdware_info->type_mask[*dev_type_p];
                if (stat->st_count >
-                   stat->disabled_count + stat->user_disabled_count)
+                   (stat->disabled_count + stat->user_disabled_count))
                        return 0;
                return -1;
        }
 
+       /**
+        * Assumption: PCICA, PCIXCC_MCL3, CEX2C, and CEX2A are all similar in
+        * speed.
+        *
+        * PCICA and CEX2A do NOT co-exist, so it would be either one or the
+        * other present.
+        */
        stat = &z90crypt.hdware_info->type_mask[PCICA];
-       if (stat->st_count > stat->disabled_count + stat->user_disabled_count) {
-               *dev_type_p = PCICA;
+       PCICA_avail = stat->st_count -
+                       (stat->disabled_count + stat->user_disabled_count);
+       stat = &z90crypt.hdware_info->type_mask[PCIXCC_MCL3];
+       PCIXCC_MCL3_avail = stat->st_count -
+                       (stat->disabled_count + stat->user_disabled_count);
+       stat = &z90crypt.hdware_info->type_mask[CEX2C];
+       CEX2C_avail = stat->st_count -
+                       (stat->disabled_count + stat->user_disabled_count);
+       stat = &z90crypt.hdware_info->type_mask[CEX2A];
+       CEX2A_avail = stat->st_count -
+                       (stat->disabled_count + stat->user_disabled_count);
+       if (PCICA_avail || PCIXCC_MCL3_avail || CEX2C_avail || CEX2A_avail) {
+               /**
+                * bitlength is a factor, PCICA or CEX2A are the most capable,
+                * even with the new MCL for PCIXCC.
+                */
+               if ((bytelength < PCIXCC_MIN_MOD_SIZE) ||
+                   (!ext_bitlens && (bytelength < OLD_PCIXCC_MIN_MOD_SIZE))) {
+                       if (PCICA_avail) {
+                               *dev_type_p = PCICA;
+                               return 0;
+                       }
+                       if (CEX2A_avail) {
+                               *dev_type_p = CEX2A;
+                               return 0;
+                       }
+                       return -1;
+               }
+
+               index_to_use = count % (PCICA_avail + PCIXCC_MCL3_avail +
+                                       CEX2C_avail + CEX2A_avail);
+               if (index_to_use < PCICA_avail)
+                       *dev_type_p = PCICA;
+               else if (index_to_use < (PCICA_avail + PCIXCC_MCL3_avail))
+                       *dev_type_p = PCIXCC_MCL3;
+               else if (index_to_use < (PCICA_avail + PCIXCC_MCL3_avail +
+                                        CEX2C_avail))
+                       *dev_type_p = CEX2C;
+               else
+                       *dev_type_p = CEX2A;
+               count++;
                return 0;
        }
 
-       stat = &z90crypt.hdware_info->type_mask[PCIXCC];
-       if (stat->st_count > stat->disabled_count + stat->user_disabled_count) {
-               *dev_type_p = PCIXCC;
+       /* Less than OLD_PCIXCC_MIN_MOD_SIZE cannot go to a PCIXCC_MCL2 */
+       if (bytelength < OLD_PCIXCC_MIN_MOD_SIZE)
+               return -1;
+       stat = &z90crypt.hdware_info->type_mask[PCIXCC_MCL2];
+       if (stat->st_count >
+           (stat->disabled_count + stat->user_disabled_count)) {
+               *dev_type_p = PCIXCC_MCL2;
                return 0;
        }
 
+       /**
+        * Less than PCICC_MIN_MOD_SIZE or more than OLD_PCICC_MAX_MOD_SIZE
+        * (if we don't have the MCL applied and the newer bitlengths enabled)
+        * cannot go to a PCICC
+        */
+       if ((bytelength < PCICC_MIN_MOD_SIZE) ||
+           (!ext_bitlens && (bytelength > OLD_PCICC_MAX_MOD_SIZE))) {
+               return -1;
+       }
        stat = &z90crypt.hdware_info->type_mask[PCICC];
-       if (stat->st_count > stat->disabled_count + stat->user_disabled_count) {
+       if (stat->st_count >
+           (stat->disabled_count + stat->user_disabled_count)) {
                *dev_type_p = PCICC;
                return 0;
        }
@@ -1087,7 +1108,7 @@ select_device_type(int *dev_type_p)
  * Try the selected number, then the selected type (can be ANYDEV)
  */
 static inline int
-select_device(int *dev_type_p, int *device_nr_p)
+select_device(int *dev_type_p, int *device_nr_p, int bytelength)
 {
        int i, indx, devTp, low_count, low_indx;
        struct device_x *index_p;
@@ -1099,9 +1120,9 @@ select_device(int *dev_type_p, int *device_nr_p)
                dev_ptr = z90crypt.device_p[*device_nr_p];
 
                if (dev_ptr &&
-                   dev_ptr->dev_stat != DEV_GONE &&
-                   dev_ptr->disabled == 0 &&
-                   dev_ptr->user_disabled == 0) {
+                   (dev_ptr->dev_stat != DEV_GONE) &&
+                   (dev_ptr->disabled == 0) &&
+                   (dev_ptr->user_disabled == 0)) {
                        PDEBUG("selected by number, index = %d\n",
                               *device_nr_p);
                        *dev_type_p = dev_ptr->dev_type;
@@ -1111,7 +1132,7 @@ select_device(int *dev_type_p, int *device_nr_p)
        *device_nr_p = -1;
        PDEBUG("trying type = %d\n", *dev_type_p);
        devTp = *dev_type_p;
-       if (select_device_type(&devTp) == -1) {
+       if (select_device_type(&devTp, bytelength) == -1) {
                PDEBUG("failed to select by type\n");
                return -1;
        }
@@ -1123,11 +1144,11 @@ select_device(int *dev_type_p, int *device_nr_p)
                indx = index_p->device_index[i];
                dev_ptr = z90crypt.device_p[indx];
                if (dev_ptr &&
-                   dev_ptr->dev_stat != DEV_GONE &&
-                   dev_ptr->disabled == 0 &&
-                   dev_ptr->user_disabled == 0 &&
-                   devTp == dev_ptr->dev_type &&
-                   low_count > dev_ptr->dev_caller_count) {
+                   (dev_ptr->dev_stat != DEV_GONE) &&
+                   (dev_ptr->disabled == 0) &&
+                   (dev_ptr->user_disabled == 0) &&
+                   (devTp == dev_ptr->dev_type) &&
+                   (low_count > dev_ptr->dev_caller_count)) {
                        low_count = dev_ptr->dev_caller_count;
                        low_indx = indx;
                }
@@ -1142,12 +1163,13 @@ send_to_crypto_device(struct work_element *we_p)
        struct caller *caller_p;
        struct device *device_p;
        int dev_nr;
+       int bytelen = ((struct ica_rsa_modexpo *)we_p->buffer)->inputdatalength;
 
        if (!we_p->requestptr)
                return SEN_FATAL_ERROR;
        caller_p = (struct caller *)we_p->requestptr;
        dev_nr = we_p->devindex;
-       if (select_device(&we_p->devtype, &dev_nr) == -1) {
+       if (select_device(&we_p->devtype, &dev_nr, bytelen) == -1) {
                if (z90crypt.hdware_info->hdware_mask.st_count != 0)
                        return SEN_RETRY;
                else
@@ -1296,15 +1318,6 @@ z90crypt_process_results(struct work_element *we_p, char __user *buf)
 static unsigned char NULL_psmid[8] =
 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
-/**
- * MIN_MOD_SIZE is a PCICC and PCIXCC limit.
- * MAX_PCICC_MOD_SIZE is a hard limit for the PCICC.
- * MAX_MOD_SIZE is a hard limit for the PCIXCC and PCICA.
- */
-#define MIN_MOD_SIZE 64
-#define MAX_PCICC_MOD_SIZE 128
-#define MAX_MOD_SIZE 256
-
 /**
  * Used in device configuration functions
  */
@@ -1361,7 +1374,8 @@ build_caller(struct work_element *we_p, short function)
        struct caller *caller_p = (struct caller *)we_p->requestptr;
 
        if ((we_p->devtype != PCICC) && (we_p->devtype != PCICA) &&
-           (we_p->devtype != PCIXCC))
+           (we_p->devtype != PCIXCC_MCL2) && (we_p->devtype != PCIXCC_MCL3) &&
+           (we_p->devtype != CEX2C) && (we_p->devtype != CEX2A))
                return SEN_NOT_AVAIL;
 
        memcpy(caller_p->caller_id, we_p->caller_id,
@@ -1393,9 +1407,8 @@ unbuild_caller(struct device *device_p, struct caller *caller_p)
                return;
        if (caller_p->caller_liste.next && caller_p->caller_liste.prev)
                if (!list_empty(&caller_p->caller_liste)) {
-                       list_del(&caller_p->caller_liste);
+                       list_del_init(&caller_p->caller_liste);
                        device_p->dev_caller_count--;
-                       INIT_LIST_HEAD(&caller_p->caller_liste);
                }
        memset(caller_p->caller_id, 0, sizeof(caller_p->caller_id));
 }
@@ -1430,7 +1443,9 @@ get_crypto_request_buffer(struct work_element *we_p)
        }
 
        if ((we_p->devtype != PCICA) && (we_p->devtype != PCICC) &&
-           (we_p->devtype != PCIXCC) && (we_p->devtype != ANYDEV)) {
+           (we_p->devtype != PCIXCC_MCL2) && (we_p->devtype != PCIXCC_MCL3) &&
+           (we_p->devtype != CEX2C) && (we_p->devtype != CEX2A) &&
+           (we_p->devtype != ANYDEV)) {
                PRINTK("invalid device type\n");
                return SEN_USER_ERROR;
        }
@@ -1494,7 +1509,7 @@ get_crypto_request_buffer(struct work_element *we_p)
        if (rv != 0)
                return rv;
 
-       if (select_device_type(&we_p->devtype) < 0)
+       if (select_device_type(&we_p->devtype, mex_p->inputdatalength) < 0)
                return SEN_NOT_AVAIL;
 
        temp_buffer = (unsigned char *)we_p + sizeof(struct work_element) +
@@ -1505,18 +1520,25 @@ get_crypto_request_buffer(struct work_element *we_p)
 
        function = PCI_FUNC_KEY_ENCRYPT;
        switch (we_p->devtype) {
-       /* PCICA does everything with a simple RSA mod-expo operation */
+       /* PCICA and CEX2A do everything with a simple RSA mod-expo operation */
        case PCICA:
+       case CEX2A:
                function = PCI_FUNC_KEY_ENCRYPT;
                break;
        /**
-        * PCIXCC does all Mod-Expo form with a simple RSA mod-expo
+        * PCIXCC_MCL2 does all Mod-Expo form with a simple RSA mod-expo
         * operation, and all CRT forms with a PKCS-1.2 format decrypt.
+        * PCIXCC_MCL3 and CEX2C do all Mod-Expo and CRT forms with a simple RSA
+        * mod-expo operation
         */
-       case PCIXCC:
-               /* Anything less than MIN_MOD_SIZE MUST go to a PCICA */
-               if (mex_p->inputdatalength < MIN_MOD_SIZE)
-                       return SEN_NOT_AVAIL;
+       case PCIXCC_MCL2:
+               if (we_p->funccode == ICARSAMODEXPO)
+                       function = PCI_FUNC_KEY_ENCRYPT;
+               else
+                       function = PCI_FUNC_KEY_DECRYPT;
+               break;
+       case PCIXCC_MCL3:
+       case CEX2C:
                if (we_p->funccode == ICARSAMODEXPO)
                        function = PCI_FUNC_KEY_ENCRYPT;
                else
@@ -1526,14 +1548,6 @@ get_crypto_request_buffer(struct work_element *we_p)
         * PCICC does everything as a PKCS-1.2 format request
         */
        case PCICC:
-               /* Anything less than MIN_MOD_SIZE MUST go to a PCICA */
-               if (mex_p->inputdatalength < MIN_MOD_SIZE) {
-                       return SEN_NOT_AVAIL;
-               }
-               /* Anythings over MAX_PCICC_MOD_SIZE MUST go to a PCICA */
-               if (mex_p->inputdatalength > MAX_PCICC_MOD_SIZE) {
-                       return SEN_NOT_AVAIL;
-               }
                /* PCICC cannot handle input that is is PKCS#1.1 padded */
                if (is_PKCS11_padded(temp_buffer, mex_p->inputdatalength)) {
                        return SEN_NOT_AVAIL;
@@ -1593,6 +1607,7 @@ z90crypt_prepare(struct work_element *we_p, unsigned int funccode,
                rv = -ENODEV;
                break;
        case SEN_NOT_AVAIL:
+       case EGETBUFF:
                rv = -EGETBUFF;
                break;
        default:
@@ -1613,14 +1628,14 @@ purge_work_element(struct work_element *we_p)
        spin_lock_irq(&queuespinlock);
        list_for_each(lptr, &request_list) {
                if (lptr == &we_p->liste) {
-                       list_del(lptr);
+                       list_del_init(lptr);
                        requestq_count--;
                        break;
                }
        }
        list_for_each(lptr, &pending_list) {
                if (lptr == &we_p->liste) {
-                       list_del(lptr);
+                       list_del_init(lptr);
                        pendingq_count--;
                        break;
                }
@@ -1659,14 +1674,14 @@ z90crypt_rsa(struct priv_data *private_data_p, pid_t pid,
        if ((we_p->status[0] & STAT_FAILED)) {
                switch (rv) {
                /**
-                * EINVAL *after* receive is almost always padding
-                * error issued by a PCICC or PCIXCC. We convert this
-                * return value to -EGETBUFF which should trigger a
-                * fallback to software.
+                * EINVAL *after* receive is almost always a padding error or
+                * length error issued by a coprocessor (not an accelerator).
+                * We convert this return value to -EGETBUFF which should
+                * trigger a fallback to software.
                 */
                case -EINVAL:
-                       if ((we_p->devtype == PCICC) ||
-                           (we_p->devtype == PCIXCC))
+                       if ((we_p->devtype != PCICA) &&
+                           (we_p->devtype != CEX2A))
                                rv = -EGETBUFF;
                        break;
                case -ETIMEOUT:
@@ -1700,9 +1715,8 @@ z90crypt_rsa(struct priv_data *private_data_p, pid_t pid,
  * This function is a little long, but it's really just one large switch
  * statement.
  */
-static int
-z90crypt_ioctl(struct inode *inode, struct file *filp,
-              unsigned int cmd, unsigned long arg)
+static long
+z90crypt_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
        struct priv_data *private_data_p = filp->private_data;
        unsigned char *status;
@@ -1710,7 +1724,8 @@ z90crypt_ioctl(struct inode *inode, struct file *filp,
        unsigned int *reqcnt;
        struct ica_z90_status *pstat;
        int ret, i, loopLim, tempstat;
-       static int deprecated_msg_count = 0;
+       static int deprecated_msg_count1 = 0;
+       static int deprecated_msg_count2 = 0;
 
        PDEBUG("filp %p (PID %d), cmd 0x%08X\n", filp, PID(), cmd);
        PDEBUG("cmd 0x%08X: dir %s, size 0x%04X, type 0x%02X, nr 0x%02X\n",
@@ -1765,8 +1780,26 @@ z90crypt_ioctl(struct inode *inode, struct file *filp,
                        ret = -EFAULT;
                break;
 
-       case Z90STAT_PCIXCCCOUNT:
-               tempstat = get_status_PCIXCCcount();
+       case Z90STAT_PCIXCCMCL2COUNT:
+               tempstat = get_status_PCIXCCMCL2count();
+               if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
+                       ret = -EFAULT;
+               break;
+
+       case Z90STAT_PCIXCCMCL3COUNT:
+               tempstat = get_status_PCIXCCMCL3count();
+               if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
+                       ret = -EFAULT;
+               break;
+
+       case Z90STAT_CEX2CCOUNT:
+               tempstat = get_status_CEX2Ccount();
+               if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
+                       ret = -EFAULT;
+               break;
+
+       case Z90STAT_CEX2ACOUNT:
+               tempstat = get_status_CEX2Acount();
                if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
                        ret = -EFAULT;
                break;
@@ -1838,10 +1871,10 @@ z90crypt_ioctl(struct inode *inode, struct file *filp,
 
                /* THIS IS DEPRECATED.  USE THE NEW STATUS CALLS */
        case ICAZ90STATUS:
-               if (deprecated_msg_count < 100) {
+               if (deprecated_msg_count1 < 20) {
                        PRINTK("deprecated call to ioctl (ICAZ90STATUS)!\n");
-                       deprecated_msg_count++;
-                       if (deprecated_msg_count == 100)
+                       deprecated_msg_count1++;
+                       if (deprecated_msg_count1 == 20)
                                PRINTK("No longer issuing messages related to "
                                       "deprecated call to ICAZ90STATUS.\n");
                }
@@ -1869,6 +1902,21 @@ z90crypt_ioctl(struct inode *inode, struct file *filp,
                kfree(pstat);
                break;
 
+               /* THIS IS DEPRECATED.  USE THE NEW STATUS CALLS */
+       case Z90STAT_PCIXCCCOUNT:
+               if (deprecated_msg_count2 < 20) {
+                       PRINTK("deprecated ioctl (Z90STAT_PCIXCCCOUNT)!\n");
+                       deprecated_msg_count2++;
+                       if (deprecated_msg_count2 == 20)
+                               PRINTK("No longer issuing messages about depre"
+                                      "cated ioctl Z90STAT_PCIXCCCOUNT.\n");
+               }
+
+               tempstat = get_status_PCIXCCcount();
+               if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
+                       ret = -EFAULT;
+               break;
+
        case Z90QUIESCE:
                if (current->euid != 0) {
                        PRINTK("QUIESCE fails: euid %d\n",
@@ -1990,8 +2038,14 @@ z90crypt_status(char *resp_buff, char **start, off_t offset,
                get_status_PCICAcount());
        len += sprintf(resp_buff+len, "PCICC count: %d\n",
                get_status_PCICCcount());
-       len += sprintf(resp_buff+len, "PCIXCC count: %d\n",
-               get_status_PCIXCCcount());
+       len += sprintf(resp_buff+len, "PCIXCC MCL2 count: %d\n",
+               get_status_PCIXCCMCL2count());
+       len += sprintf(resp_buff+len, "PCIXCC MCL3 count: %d\n",
+               get_status_PCIXCCMCL3count());
+       len += sprintf(resp_buff+len, "CEX2C count: %d\n",
+               get_status_CEX2Ccount());
+       len += sprintf(resp_buff+len, "CEX2A count: %d\n",
+               get_status_CEX2Acount());
        len += sprintf(resp_buff+len, "requestq count: %d\n",
                get_status_requestq_count());
        len += sprintf(resp_buff+len, "pendingq count: %d\n",
@@ -1999,7 +2053,8 @@ z90crypt_status(char *resp_buff, char **start, off_t offset,
        len += sprintf(resp_buff+len, "Total open handles: %d\n\n",
                get_status_totalopen_count());
        len += sprinthx(
-               "Online devices: 1 means PCICA, 2 means PCICC, 3 means PCIXCC",
+               "Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) "
+               "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A",
                resp_buff+len,
                get_status_status_mask(workarea),
                Z90CRYPT_NUM_APS);
@@ -2047,73 +2102,15 @@ enable_card(int card_index)
        z90crypt.hdware_info->type_mask[devp->dev_type].user_disabled_count--;
 }
 
-static inline int
-scan_char(unsigned char *bf, unsigned int len,
-         unsigned int *offs, unsigned int *p_eof, unsigned char c)
-{
-       unsigned int i, found;
-
-       found = 0;
-       for (i = 0; i < len; i++) {
-               if (bf[i] == c) {
-                       found = 1;
-                       break;
-               }
-               if (bf[i] == '\0') {
-                       *p_eof = 1;
-                       break;
-               }
-               if (bf[i] == '\n') {
-                       break;
-               }
-       }
-       *offs = i+1;
-       return found;
-}
-
-static inline int
-scan_string(unsigned char *bf, unsigned int len,
-           unsigned int *offs, unsigned int *p_eof, unsigned char *s)
-{
-       unsigned int temp_len, temp_offs, found, eof;
-
-       temp_len = temp_offs = found = eof = 0;
-       while (!eof && !found) {
-               found = scan_char(bf+temp_len, len-temp_len,
-                                 &temp_offs, &eof, *s);
-
-               temp_len += temp_offs;
-               if (eof) {
-                       found = 0;
-                       break;
-               }
-
-               if (found) {
-                       if (len >= temp_offs+strlen(s)) {
-                               found = !strncmp(bf+temp_len-1, s, strlen(s));
-                               if (found) {
-                                       *offs = temp_len+strlen(s)-1;
-                                       break;
-                               }
-                       } else {
-                               found = 0;
-                               *p_eof = 1;
-                               break;
-                       }
-               }
-       }
-       return found;
-}
-
 static int
 z90crypt_status_write(struct file *file, const char __user *buffer,
                      unsigned long count, void *data)
 {
-       int i, j, len, offs, found, eof;
-       unsigned char *lbuf;
+       int j, eol;
+       unsigned char *lbuf, *ptr;
        unsigned int local_count;
 
-#define LBUFSIZE 600
+#define LBUFSIZE 1200
        lbuf = kmalloc(LBUFSIZE, GFP_KERNEL);
        if (!lbuf) {
                PRINTK("kmalloc failed!\n");
@@ -2130,47 +2127,47 @@ z90crypt_status_write(struct file *file, const char __user *buffer,
                return -EFAULT;
        }
 
-       lbuf[local_count-1] = '\0';
+       lbuf[local_count] = '\0';
 
-       len = 0;
-       eof = 0;
-       found = 0;
-       while (!eof) {
-               found = scan_string(lbuf+len, local_count-len, &offs, &eof,
-                                   "Online devices");
-               len += offs;
-               if (found == 1)
-                       break;
+       ptr = strstr(lbuf, "Online devices");
+       if (ptr == 0) {
+               PRINTK("Unable to parse data (missing \"Online devices\")\n");
+               kfree(lbuf);
+               return count;
        }
 
-       if (eof) {
+       ptr = strstr(ptr, "\n");
+       if (ptr == 0) {
+               PRINTK("Unable to parse data (missing newline after \"Online devices\")\n");
                kfree(lbuf);
                return count;
        }
+       ptr++;
 
-       if (found)
-               found = scan_char(lbuf+len, local_count-len, &offs, &eof, '\n');
-
-       if (!found || eof) {
+       if (strstr(ptr, "Waiting work element counts") == NULL) {
+               PRINTK("Unable to parse data (missing \"Waiting work element counts\")\n");
                kfree(lbuf);
                return count;
        }
 
-       len += offs;
        j = 0;
-       for (i = 0; i < 80; i++) {
-               switch (*(lbuf+len+i)) {
+       eol = 0;
+       while ((j < 64) && (*ptr != '\0')) {
+               switch (*ptr) {
                case '\t':
                case ' ':
                        break;
                case '\n':
                default:
-                       eof = 1;
+                       eol = 1;
                        break;
-               case '0':
-               case '1':
-               case '2':
-               case '3':
+               case '0':       // no device
+               case '1':       // PCICA
+               case '2':       // PCICC
+               case '3':       // PCIXCC_MCL2
+               case '4':       // PCIXCC_MCL3
+               case '5':       // CEX2C
+               case '6':       // CEX2A
                        j++;
                        break;
                case 'd':
@@ -2184,8 +2181,9 @@ z90crypt_status_write(struct file *file, const char __user *buffer,
                        j++;
                        break;
                }
-               if (eof)
+               if (eol)
                        break;
+               ptr++;
        }
 
        kfree(lbuf);
@@ -2228,13 +2226,12 @@ receive_from_crypto_device(int index, unsigned char *psmid, int *buff_len_p,
        dev_ptr = z90crypt.device_p[index];
        rv = 0;
        do {
-               PDEBUG("Dequeue called for device %d\n", index);
                if (!dev_ptr || dev_ptr->disabled) {
-                       rv = REC_NO_RESPONSE;
+                       rv = REC_NO_WORK; // a disabled device can't return work
                        break;
                }
                if (dev_ptr->dev_self_x != index) {
-                       PRINTK("Corrupt dev ptr in receive_from_AP\n");
+                       PRINTKC("Corrupt dev ptr\n");
                        z90crypt.terminating = 1;
                        rv = REC_FATAL_ERROR;
                        break;
@@ -2244,6 +2241,7 @@ receive_from_crypto_device(int index, unsigned char *psmid, int *buff_len_p,
                        PRINTK("dev_resp_l = %d, dev_resp_p = %p\n",
                               dev_ptr->dev_resp_l, dev_ptr->dev_resp_p);
                } else {
+                       PDEBUG("Dequeue called for device %d\n", index);
                        dv = receive_from_AP(index, z90crypt.cdx,
                                             dev_ptr->dev_resp_l,
                                             dev_ptr->dev_resp_p, psmid);
@@ -2283,15 +2281,18 @@ receive_from_crypto_device(int index, unsigned char *psmid, int *buff_len_p,
                        if (!memcmp(caller_p->caller_id, psmid,
                                    sizeof(caller_p->caller_id))) {
                                if (!list_empty(&caller_p->caller_liste)) {
-                                       list_del(ptr);
+                                       list_del_init(ptr);
                                        dev_ptr->dev_caller_count--;
-                                       INIT_LIST_HEAD(&caller_p->caller_liste);
                                        break;
                                }
                        }
                        caller_p = 0;
                }
                if (!caller_p) {
+                       PRINTKW("Unable to locate PSMID %02X%02X%02X%02X%02X"
+                               "%02X%02X%02X in device list\n",
+                               psmid[0], psmid[1], psmid[2], psmid[3],
+                               psmid[4], psmid[5], psmid[6], psmid[7]);
                        rv = REC_USER_GONE;
                        break;
                }
@@ -2300,22 +2301,22 @@ receive_from_crypto_device(int index, unsigned char *psmid, int *buff_len_p,
                rv = convert_response(dev_ptr->dev_resp_p,
                                      caller_p->caller_buf_p, buff_len_p, buff);
                switch (rv) {
+               case REC_USE_PCICA:
+                       break;
                case REC_OPERAND_INV:
-                       PDEBUG("dev %d: user error %d\n", index, rv);
+               case REC_OPERAND_SIZE:
+               case REC_EVEN_MOD:
+               case REC_INVALID_PAD:
+                       PDEBUG("device %d: 'user error' %d\n", index, rv);
                        break;
                case WRONG_DEVICE_TYPE:
                case REC_HARDWAR_ERR:
                case REC_BAD_MESSAGE:
-                       PRINTK("dev %d: hardware error %d\n",
-                              index, rv);
+                       PRINTKW("device %d: hardware error %d\n", index, rv);
                        rv = REC_NO_RESPONSE;
                        break;
-               case REC_RELEASED:
-                       PDEBUG("dev %d: REC_RELEASED = %d\n",
-                              index, rv);
-                       break;
                default:
-                       PDEBUG("dev %d: rv = %d\n", index, rv);
+                       PDEBUG("device %d: rv = %d\n", index, rv);
                        break;
                }
        } while (0);
@@ -2329,6 +2330,7 @@ receive_from_crypto_device(int index, unsigned char *psmid, int *buff_len_p,
                        PRINTK("Zero *buff_len_p\n");
                break;
        case REC_NO_RESPONSE:
+               PRINTKW("Removing device %d from availability\n", index);
                remove_device(dev_ptr);
                break;
        }
@@ -2349,7 +2351,7 @@ helper_send_work(int index)
                return;
        requestq_count--;
        rq_p = list_entry(request_list.next, struct work_element, liste);
-       list_del(&rq_p->liste);
+       list_del_init(&rq_p->liste);
        rq_p->audit[1] |= FP_REMREQUEST;
        if (rq_p->devtype == SHRT2DEVPTR(index)->dev_type) {
                rq_p->devindex = SHRT2LONG(index);
@@ -2408,7 +2410,7 @@ helper_handle_work_element(int index, unsigned char psmid[8], int rc,
        list_for_each_safe(lptr, tptr, &pending_list) {
                pq_p = list_entry(lptr, struct work_element, liste);
                if (!memcmp(pq_p->caller_id, psmid, sizeof(pq_p->caller_id))) {
-                       list_del(lptr);
+                       list_del_init(lptr);
                        pendingq_count--;
                        pq_p->audit[1] |= FP_NOTPENDING;
                        break;
@@ -2441,6 +2443,10 @@ helper_handle_work_element(int index, unsigned char psmid[8], int rc,
                        pq_p->retcode = -EINVAL;
                        pq_p->status[0] |= STAT_FAILED;
                        break;
+               case REC_USE_PCICA:
+                       pq_p->retcode = -ERESTARTSYS;
+                       pq_p->status[0] |= STAT_FAILED;
+                       break;
                case REC_NO_RESPONSE:
                default:
                        if (z90crypt.mask.st_count > 1)
@@ -2461,7 +2467,7 @@ helper_handle_work_element(int index, unsigned char psmid[8], int rc,
  * return TRUE if the work element should be removed from the queue
  */
 static inline int
-helper_receive_rc(int index, int *rc_p, int *workavail_p)
+helper_receive_rc(int index, int *rc_p)
 {
        switch (*rc_p) {
        case 0:
@@ -2469,26 +2475,26 @@ helper_receive_rc(int index, int *rc_p, int *workavail_p)
        case REC_OPERAND_SIZE:
        case REC_EVEN_MOD:
        case REC_INVALID_PAD:
-               return 1;
+       case REC_USE_PCICA:
+               break;
 
        case REC_BUSY:
        case REC_NO_WORK:
        case REC_EMPTY:
        case REC_RETRY_DEV:
        case REC_FATAL_ERROR:
-               break;
+               return 0;
 
        case REC_NO_RESPONSE:
-               *workavail_p = 0;
                break;
 
        default:
-               PRINTK("rc %d, device %d\n", *rc_p, SHRT2LONG(index));
+               PRINTK("rc %d, device %d converted to REC_NO_RESPONSE\n",
+                      *rc_p, SHRT2LONG(index));
                *rc_p = REC_NO_RESPONSE;
-               *workavail_p = 0;
                break;
        }
-       return 0;
+       return 1;
 }
 
 static inline void
@@ -2503,21 +2509,18 @@ z90crypt_schedule_reader_timer(void)
 static void
 z90crypt_reader_task(unsigned long ptr)
 {
-       int workavail, remaining, index, rc, buff_len;
+       int workavail, index, rc, buff_len;
        unsigned char   psmid[8];
        unsigned char __user *resp_addr;
        static unsigned char buff[1024];
 
-       PDEBUG("jiffies %ld\n", jiffies);
-
        /**
         * we use workavail = 2 to ensure 2 passes with nothing dequeued before
-        * exiting the loop. If remaining == 0 after the loop, there is no work
-        * remaining on the queues.
+        * exiting the loop. If (pendingq_count+requestq_count) == 0 after the
+        * loop, there is no work remaining on the queues.
         */
        resp_addr = 0;
        workavail = 2;
-       remaining = 0;
        buff_len = 0;
        while (workavail) {
                workavail--;
@@ -2535,7 +2538,7 @@ z90crypt_reader_task(unsigned long ptr)
                                                        &resp_addr);
                        PDEBUG("Dequeued: rc = %d.\n", rc);
 
-                       if (helper_receive_rc(index, &rc, &workavail)) {
+                       if (helper_receive_rc(index, &rc)) {
                                if (rc != REC_NO_RESPONSE) {
                                        helper_send_work(index);
                                        workavail = 2;
@@ -2547,19 +2550,14 @@ z90crypt_reader_task(unsigned long ptr)
                        }
 
                        if (rc == REC_FATAL_ERROR)
-                               remaining = 0;
-                       else if (rc != REC_NO_RESPONSE)
-                               remaining +=
-                                       SHRT2DEVPTR(index)->dev_caller_count;
+                               PRINTKW("REC_FATAL_ERROR from device %d!\n",
+                                       SHRT2LONG(index));
                }
                spin_unlock_irq(&queuespinlock);
        }
 
-       if (remaining) {
-               spin_lock_irq(&queuespinlock);
+       if (pendingq_count + requestq_count)
                z90crypt_schedule_reader_timer();
-               spin_unlock_irq(&queuespinlock);
-       }
 }
 
 static inline void
@@ -2606,7 +2604,7 @@ helper_drain_queues(void)
                pq_p->status[0] |= STAT_FAILED;
                unbuild_caller(LONG2DEVPTR(pq_p->devindex),
                               (struct caller *)pq_p->requestptr);
-               list_del(lptr);
+               list_del_init(lptr);
                pendingq_count--;
                pq_p->audit[1] |= FP_NOTPENDING;
                pq_p->audit[1] |= FP_AWAKENING;
@@ -2618,7 +2616,7 @@ helper_drain_queues(void)
                pq_p = list_entry(lptr, struct work_element, liste);
                pq_p->retcode = -ENODEV;
                pq_p->status[0] |= STAT_FAILED;
-               list_del(lptr);
+               list_del_init(lptr);
                requestq_count--;
                pq_p->audit[1] |= FP_REMREQUEST;
                pq_p->audit[1] |= FP_AWAKENING;
@@ -2640,12 +2638,21 @@ helper_timeout_requests(void)
                pq_p = list_entry(lptr, struct work_element, liste);
                if (pq_p->requestsent >= timelimit)
                        break;
+               PRINTKW("Purging(PQ) PSMID %02X%02X%02X%02X%02X%02X%02X%02X\n",
+                      ((struct caller *)pq_p->requestptr)->caller_id[0],
+                      ((struct caller *)pq_p->requestptr)->caller_id[1],
+                      ((struct caller *)pq_p->requestptr)->caller_id[2],
+                      ((struct caller *)pq_p->requestptr)->caller_id[3],
+                      ((struct caller *)pq_p->requestptr)->caller_id[4],
+                      ((struct caller *)pq_p->requestptr)->caller_id[5],
+                      ((struct caller *)pq_p->requestptr)->caller_id[6],
+                      ((struct caller *)pq_p->requestptr)->caller_id[7]);
                pq_p->retcode = -ETIMEOUT;
                pq_p->status[0] |= STAT_FAILED;
                /* get this off any caller queue it may be on */
                unbuild_caller(LONG2DEVPTR(pq_p->devindex),
                               (struct caller *) pq_p->requestptr);
-               list_del(lptr);
+               list_del_init(lptr);
                pendingq_count--;
                pq_p->audit[1] |= FP_TIMEDOUT;
                pq_p->audit[1] |= FP_NOTPENDING;
@@ -2663,9 +2670,18 @@ helper_timeout_requests(void)
                        pq_p = list_entry(lptr, struct work_element, liste);
                        if (pq_p->requestsent >= timelimit)
                                break;
+               PRINTKW("Purging(RQ) PSMID %02X%02X%02X%02X%02X%02X%02X%02X\n",
+                      ((struct caller *)pq_p->requestptr)->caller_id[0],
+                      ((struct caller *)pq_p->requestptr)->caller_id[1],
+                      ((struct caller *)pq_p->requestptr)->caller_id[2],
+                      ((struct caller *)pq_p->requestptr)->caller_id[3],
+                      ((struct caller *)pq_p->requestptr)->caller_id[4],
+                      ((struct caller *)pq_p->requestptr)->caller_id[5],
+                      ((struct caller *)pq_p->requestptr)->caller_id[6],
+                      ((struct caller *)pq_p->requestptr)->caller_id[7]);
                        pq_p->retcode = -ETIMEOUT;
                        pq_p->status[0] |= STAT_FAILED;
-                       list_del(lptr);
+                       list_del_init(lptr);
                        requestq_count--;
                        pq_p->audit[1] |= FP_TIMEDOUT;
                        pq_p->audit[1] |= FP_REMREQUEST;
@@ -2720,13 +2736,11 @@ create_z90crypt(int *cdx_p)
        z90crypt.max_count = Z90CRYPT_NUM_DEVS;
        z90crypt.cdx = *cdx_p;
 
-       hdware_blk_p = (struct hdware_block *)
-               kmalloc(sizeof(struct hdware_block), GFP_ATOMIC);
+       hdware_blk_p = kzalloc(sizeof(struct hdware_block), GFP_ATOMIC);
        if (!hdware_blk_p) {
                PDEBUG("kmalloc for hardware block failed\n");
                return ENOMEM;
        }
-       memset(hdware_blk_p, 0x00, sizeof(struct hdware_block));
        z90crypt.hdware_info = hdware_blk_p;
 
        return 0;
@@ -2737,15 +2751,15 @@ helper_scan_devices(int cdx_array[16], int *cdx_p, int *correct_cdx_found)
 {
        enum hdstat hd_stat;
        int q_depth, dev_type;
-       int i, j, k;
+       int indx, chkdom, numdomains;
 
-       q_depth = dev_type = k = 0;
-       for (i = 0; i < z90crypt.max_count; i++) {
+       q_depth = dev_type = numdomains = 0;
+       for (chkdom = 0; chkdom <= 15; cdx_array[chkdom++] = -1);
+       for (indx = 0; indx < z90crypt.max_count; indx++) {
                hd_stat = HD_NOT_THERE;
-               for (j = 0; j <= 15; cdx_array[j++] = -1);
-               k = 0;
-               for (j = 0; j <= 15; j++) {
-                       hd_stat = query_online(i, j, MAX_RESET,
+               numdomains = 0;
+               for (chkdom = 0; chkdom <= 15; chkdom++) {
+                       hd_stat = query_online(indx, chkdom, MAX_RESET,
                                               &q_depth, &dev_type);
                        if (hd_stat == HD_TSQ_EXCEPTION) {
                                z90crypt.terminating = 1;
@@ -2753,29 +2767,30 @@ helper_scan_devices(int cdx_array[16], int *cdx_p, int *correct_cdx_found)
                                break;
                        }
                        if (hd_stat == HD_ONLINE) {
-                               cdx_array[k++] = j;
-                               if (*cdx_p == j) {
+                               cdx_array[numdomains++] = chkdom;
+                               if (*cdx_p == chkdom) {
                                        *correct_cdx_found  = 1;
                                        break;
                                }
                        }
                }
-               if ((*correct_cdx_found == 1) || (k != 0))
+               if ((*correct_cdx_found == 1) || (numdomains != 0))
                        break;
                if (z90crypt.terminating)
                        break;
        }
-       return k;
+       return numdomains;
 }
 
 static inline int
 probe_crypto_domain(int *cdx_p)
 {
        int cdx_array[16];
-       int correct_cdx_found, k;
+       char cdx_array_text[53], temp[5];
+       int correct_cdx_found, numdomains;
 
        correct_cdx_found = 0;
-       k = helper_scan_devices(cdx_array, cdx_p, &correct_cdx_found);
+       numdomains = helper_scan_devices(cdx_array, cdx_p, &correct_cdx_found);
 
        if (z90crypt.terminating)
                return TSQ_FATAL_ERROR;
@@ -2783,23 +2798,31 @@ probe_crypto_domain(int *cdx_p)
        if (correct_cdx_found)
                return 0;
 
-       if (k == 0) {
-               *cdx_p = 0;
-               return 0;
+       if (numdomains == 0) {
+               PRINTKW("Unable to find crypto domain: No devices found\n");
+               return Z90C_NO_DEVICES;
        }
 
-       if (k == 1) {
-               if ((*cdx_p == -1) || !z90crypt.domain_established) {
+       if (numdomains == 1) {
+               if (*cdx_p == -1) {
                        *cdx_p = cdx_array[0];
                        return 0;
                }
-               if (*cdx_p != cdx_array[0]) {
-                       PRINTK("incorrect domain: specified = %d, found = %d\n",
-                              *cdx_p, cdx_array[0]);
-                       return Z90C_INCORRECT_DOMAIN;
-               }
+               PRINTKW("incorrect domain: specified = %d, found = %d\n",
+                      *cdx_p, cdx_array[0]);
+               return Z90C_INCORRECT_DOMAIN;
+       }
+
+       numdomains--;
+       sprintf(cdx_array_text, "%d", cdx_array[numdomains]);
+       while (numdomains) {
+               numdomains--;
+               sprintf(temp, ", %d", cdx_array[numdomains]);
+               strcat(cdx_array_text, temp);
        }
 
+       PRINTKW("ambiguous domain detected: specified = %d, found array = %s\n",
+               *cdx_p, cdx_array_text);
        return Z90C_AMBIGUOUS_DOMAIN;
 }
 
@@ -2807,7 +2830,7 @@ static int
 refresh_z90crypt(int *cdx_p)
 {
        int i, j, indx, rv;
-       struct status local_mask;
+       static struct status local_mask;
        struct device *devPtr;
        unsigned char oldStat, newStat;
        int return_unchanged;
@@ -2818,25 +2841,14 @@ refresh_z90crypt(int *cdx_p)
                return TSQ_FATAL_ERROR;
        rv = 0;
        if (!z90crypt.hdware_info->hdware_mask.st_count &&
-           !z90crypt.domain_established)
+           !z90crypt.domain_established) {
                rv = probe_crypto_domain(cdx_p);
-       if (z90crypt.terminating)
-               return TSQ_FATAL_ERROR;
-       if (rv) {
-               switch (rv) {
-               case Z90C_AMBIGUOUS_DOMAIN:
-                       PRINTK("ambiguous domain detected\n");
-                       break;
-               case Z90C_INCORRECT_DOMAIN:
-                       PRINTK("incorrect domain specified\n");
-                       break;
-               default:
-                       PRINTK("probe domain returned %d\n", rv);
-                       break;
-               }
-               return rv;
-       }
-       if (*cdx_p) {
+               if (z90crypt.terminating)
+                       return TSQ_FATAL_ERROR;
+               if (rv == Z90C_NO_DEVICES)
+                       return 0; // try later
+               if (rv)
+                       return rv;
                z90crypt.cdx = *cdx_p;
                z90crypt.domain_established = 1;
        }
@@ -2963,12 +2975,11 @@ create_crypto_device(int index)
                total_size = sizeof(struct device) +
                             z90crypt.q_depth_array[index] * sizeof(int);
 
-               dev_ptr = (struct device *) kmalloc(total_size, GFP_ATOMIC);
+               dev_ptr = kzalloc(total_size, GFP_ATOMIC);
                if (!dev_ptr) {
                        PRINTK("kmalloc device %d failed\n", index);
                        return ENOMEM;
                }
-               memset(dev_ptr, 0, total_size);
                dev_ptr->dev_resp_p = kmalloc(MAX_RESPONSE_SIZE, GFP_ATOMIC);
                if (!dev_ptr->dev_resp_p) {
                        kfree(dev_ptr);
@@ -2999,15 +3010,31 @@ create_crypto_device(int index)
                                return rv;
                        }
                }
+               if (dev_ptr->dev_type == PCIXCC_UNK) {
+                       rv = probe_PCIXCC_type(dev_ptr);
+                       if (rv) {
+                               PRINTK("rv = %d from probe_PCIXCC_type %d\n",
+                                      rv, index);
+                               kfree(dev_ptr->dev_resp_p);
+                               kfree(dev_ptr);
+                               return rv;
+                       }
+               }
                deviceType = dev_ptr->dev_type;
                z90crypt.dev_type_array[index] = deviceType;
                if (deviceType == PCICA)
                        z90crypt.hdware_info->device_type_array[index] = 1;
                else if (deviceType == PCICC)
                        z90crypt.hdware_info->device_type_array[index] = 2;
-               else if (deviceType == PCIXCC)
+               else if (deviceType == PCIXCC_MCL2)
                        z90crypt.hdware_info->device_type_array[index] = 3;
-               else
+               else if (deviceType == PCIXCC_MCL3)
+                       z90crypt.hdware_info->device_type_array[index] = 4;
+               else if (deviceType == CEX2C)
+                       z90crypt.hdware_info->device_type_array[index] = 5;
+               else if (deviceType == CEX2A)
+                       z90crypt.hdware_info->device_type_array[index] = 6;
+               else // No idea how this would happen.
                        z90crypt.hdware_info->device_type_array[index] = -1;
        }
 
@@ -3049,8 +3076,7 @@ destroy_crypto_device(int index)
        if (dev_ptr) {
                disabledFlag = dev_ptr->disabled;
                t = dev_ptr->dev_type;
-               if (dev_ptr->dev_resp_p)
-                       kfree(dev_ptr->dev_resp_p);
+               kfree(dev_ptr->dev_resp_p);
                kfree(dev_ptr);
        } else {
                disabledFlag = 0;
@@ -3078,15 +3104,15 @@ static void
 destroy_z90crypt(void)
 {
        int i;
+
        for (i = 0; i < z90crypt.max_count; i++)
                if (z90crypt.device_p[i])
                        destroy_crypto_device(i);
-       if (z90crypt.hdware_info)
-               kfree((void *)z90crypt.hdware_info);
+       kfree(z90crypt.hdware_info);
        memset((void *)&z90crypt, 0, sizeof(z90crypt));
 }
 
-static unsigned char static_testmsg[] = {
+static unsigned char static_testmsg[384] = {
 0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x06,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x58,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x43,
@@ -3118,7 +3144,7 @@ probe_device_type(struct device *devPtr)
 {
        int rv, dv, i, index, length;
        unsigned char psmid[8];
-       static unsigned char loc_testmsg[384];
+       static unsigned char loc_testmsg[sizeof(static_testmsg)];
 
        index = devPtr->dev_self_x;
        rv = 0;
@@ -3212,44 +3238,143 @@ probe_device_type(struct device *devPtr)
        return rv;
 }
 
-#ifdef Z90CRYPT_USE_HOTPLUG
-void
-z90crypt_hotplug_event(int dev_major, int dev_minor, int action)
-{
-#ifdef CONFIG_HOTPLUG
-       char *argv[3];
-       char *envp[6];
-       char  major[20];
-       char  minor[20];
-
-       sprintf(major, "MAJOR=%d", dev_major);
-       sprintf(minor, "MINOR=%d", dev_minor);
+static unsigned char MCL3_testmsg[] = {
+0x00,0x00,0x00,0x00,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,
+0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x00,0x00,0x00,0x01,0xC4,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x02,0x00,0x00,0x00,0x54,0x32,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE8,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x0A,0x4D,0x52,0x50,0x20,0x20,0x20,0x20,0x20,
+0x00,0x42,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,
+0x0E,0x0F,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,
+0xEE,0xFF,0xFF,0xEE,0xDD,0xCC,0xBB,0xAA,0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22,
+0x11,0x00,0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,0xFE,0xDC,0xBA,0x98,0x76,0x54,
+0x32,0x10,0x00,0x9A,0x00,0x98,0x00,0x00,0x1E,0x00,0x00,0x94,0x00,0x00,0x00,0x00,
+0x04,0x00,0x00,0x8C,0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x40,0xBA,0xE8,0x23,0x3C,
+0x75,0xF3,0x91,0x61,0xD6,0x73,0x39,0xCF,0x7B,0x6D,0x8E,0x61,0x97,0x63,0x9E,0xD9,
+0x60,0x55,0xD6,0xC7,0xEF,0xF8,0x1E,0x63,0x95,0x17,0xCC,0x28,0x45,0x60,0x11,0xC5,
+0xC4,0x4E,0x66,0xC6,0xE6,0xC3,0xDE,0x8A,0x19,0x30,0xCF,0x0E,0xD7,0xAA,0xDB,0x01,
+0xD8,0x00,0xBB,0x8F,0x39,0x9F,0x64,0x28,0xF5,0x7A,0x77,0x49,0xCC,0x6B,0xA3,0x91,
+0x97,0x70,0xE7,0x60,0x1E,0x39,0xE1,0xE5,0x33,0xE1,0x15,0x63,0x69,0x08,0x80,0x4C,
+0x67,0xC4,0x41,0x8F,0x48,0xDF,0x26,0x98,0xF1,0xD5,0x8D,0x88,0xD9,0x6A,0xA4,0x96,
+0xC5,0x84,0xD9,0x30,0x49,0x67,0x7D,0x19,0xB1,0xB3,0x45,0x4D,0xB2,0x53,0x9A,0x47,
+0x3C,0x7C,0x55,0xBF,0xCC,0x85,0x00,0x36,0xF1,0x3D,0x93,0x53
+};
 
-       argv[0] = hotplug_path;
-       argv[1] = "z90crypt";
-       argv[2] = 0;
+static int
+probe_PCIXCC_type(struct device *devPtr)
+{
+       int rv, dv, i, index, length;
+       unsigned char psmid[8];
+       static unsigned char loc_testmsg[548];
+       struct CPRBX *cprbx_p;
 
-       envp[0] = "HOME=/";
-       envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+       index = devPtr->dev_self_x;
+       rv = 0;
+       do {
+               memcpy(loc_testmsg, MCL3_testmsg, sizeof(MCL3_testmsg));
+               length = sizeof(MCL3_testmsg) - 0x0C;
+               dv = send_to_AP(index, z90crypt.cdx, length, loc_testmsg);
+               if (dv) {
+                       PDEBUG("dv returned = %d\n", dv);
+                       if (dv == DEV_SEN_EXCEPTION) {
+                               rv = SEN_FATAL_ERROR;
+                               PRINTKC("exception in send to AP %d\n", index);
+                               break;
+                       }
+                       PDEBUG("return value from send_to_AP: %d\n", rv);
+                       switch (dv) {
+                       case DEV_GONE:
+                               PDEBUG("dev %d not available\n", index);
+                               rv = SEN_NOT_AVAIL;
+                               break;
+                       case DEV_ONLINE:
+                               rv = 0;
+                               break;
+                       case DEV_EMPTY:
+                               rv = SEN_NOT_AVAIL;
+                               break;
+                       case DEV_NO_WORK:
+                               rv = SEN_FATAL_ERROR;
+                               break;
+                       case DEV_BAD_MESSAGE:
+                               rv = SEN_USER_ERROR;
+                               break;
+                       case DEV_QUEUE_FULL:
+                               rv = SEN_QUEUE_FULL;
+                               break;
+                       default:
+                               PRINTK("unknown dv=%d for dev %d\n", dv, index);
+                               rv = SEN_NOT_AVAIL;
+                               break;
+                       }
+               }
 
-       switch (action) {
-       case Z90CRYPT_HOTPLUG_ADD:
-               envp[2] = "ACTION=add";
-               break;
-       case Z90CRYPT_HOTPLUG_REMOVE:
-               envp[2] = "ACTION=remove";
-               break;
-       default:
-               BUG();
-       }
-       envp[3] = major;
-       envp[4] = minor;
-       envp[5] = 0;
+               if (rv)
+                       break;
 
-       call_usermodehelper(argv[0], argv, envp, 0);
-#endif
+               for (i = 0; i < 6; i++) {
+                       mdelay(300);
+                       dv = receive_from_AP(index, z90crypt.cdx,
+                                            devPtr->dev_resp_l,
+                                            devPtr->dev_resp_p, psmid);
+                       PDEBUG("dv returned by DQ = %d\n", dv);
+                       if (dv == DEV_REC_EXCEPTION) {
+                               rv = REC_FATAL_ERROR;
+                               PRINTKC("exception in dequeue %d\n",
+                                       index);
+                               break;
+                       }
+                       switch (dv) {
+                       case DEV_ONLINE:
+                               rv = 0;
+                               break;
+                       case DEV_EMPTY:
+                               rv = REC_EMPTY;
+                               break;
+                       case DEV_NO_WORK:
+                               rv = REC_NO_WORK;
+                               break;
+                       case DEV_BAD_MESSAGE:
+                       case DEV_GONE:
+                       default:
+                               rv = REC_NO_RESPONSE;
+                               break;
+                       }
+                       if ((rv != 0) && (rv != REC_NO_WORK))
+                               break;
+                       if (rv == 0)
+                               break;
+               }
+               if (rv)
+                       break;
+               cprbx_p = (struct CPRBX *) (devPtr->dev_resp_p + 48);
+               if ((cprbx_p->ccp_rtcode == 8) && (cprbx_p->ccp_rscode == 33)) {
+                       devPtr->dev_type = PCIXCC_MCL2;
+                       PDEBUG("device %d is MCL2\n", index);
+               } else {
+                       devPtr->dev_type = PCIXCC_MCL3;
+                       PDEBUG("device %d is MCL3\n", index);
+               }
+       } while (0);
+       /* In a general error case, the card is not marked online */
+       return rv;
 }
-#endif
 
 module_init(z90crypt_init_module);
 module_exit(z90crypt_cleanup_module);