staging: visoruislib driver used to handle requests from virtpci
authorKen Cox <jkc@redhat.com>
Tue, 4 Mar 2014 13:58:09 +0000 (07:58 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Mar 2014 00:59:31 +0000 (16:59 -0800)
The visoruislib module is a support library, used to handle requests
from virtpci.

Signed-off-by: Ken Cox <jkc@redhat.com>
Cc: Ben Romer <sparmaintainer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/unisys/Kconfig
drivers/staging/unisys/Makefile
drivers/staging/unisys/include/sparstop.h [new file with mode: 0644]
drivers/staging/unisys/uislib/Kconfig [new file with mode: 0644]
drivers/staging/unisys/uislib/Makefile [new file with mode: 0644]
drivers/staging/unisys/uislib/uislib.c [new file with mode: 0644]
drivers/staging/unisys/uislib/uisqueue.c [new file with mode: 0644]
drivers/staging/unisys/uislib/uisthread.c [new file with mode: 0644]
drivers/staging/unisys/uislib/uisutils.c [new file with mode: 0644]

index c9a10bdf73cf8311cd5970c9163303076c084e4f..0b1e0c52efbbd8a4dd3ea120f46abc11c84d5e38 100644 (file)
@@ -13,5 +13,6 @@ source "drivers/staging/unisys/visorutil/Kconfig"
 source "drivers/staging/unisys/visorchannel/Kconfig"
 source "drivers/staging/unisys/visorchipset/Kconfig"
 source "drivers/staging/unisys/channels/Kconfig"
+source "drivers/staging/unisys/uislib/Kconfig"
 
 endif # UNISYSSPAR
index e637f30cf21aa05f96f0d02fc7562284d3439acd..f9174f580ac384980f12eba1b89e1e6d81d2e24e 100644 (file)
@@ -5,4 +5,4 @@ obj-$(CONFIG_UNISYS_VISORUTIL)          += visorutil/
 obj-$(CONFIG_UNISYS_VISORCHANNEL)      += visorchannel/
 obj-$(CONFIG_UNISYS_VISORCHIPSET)      += visorchipset/
 obj-$(CONFIG_UNISYS_CHANNELSTUB)       += channels/
-
+obj-$(CONFIG_UNISYS_UISLIB)            += uislib/
diff --git a/drivers/staging/unisys/include/sparstop.h b/drivers/staging/unisys/include/sparstop.h
new file mode 100644 (file)
index 0000000..3603ac6
--- /dev/null
@@ -0,0 +1,30 @@
+/* sparstop.h
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __SPARSTOP_H__
+#define __SPARSTOP_H__
+
+#include "timskmod.h"
+#include "version.h"
+#include <linux/ctype.h>
+
+typedef void (*SPARSTOP_COMPLETE_FUNC) (void *context, int status);
+
+int sp_stop(void *context, SPARSTOP_COMPLETE_FUNC get_complete_func);
+void test_remove_stop_device(void);
+
+#endif
diff --git a/drivers/staging/unisys/uislib/Kconfig b/drivers/staging/unisys/uislib/Kconfig
new file mode 100644 (file)
index 0000000..8d87d9c
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Unisys uislib configuration
+#
+
+config UNISYS_UISLIB
+       tristate "Unisys uislib driver"
+       depends on UNISYSSPAR && UNISYS_VISORCHIPSET && UNISYS_CHANNELSTUB
+       ---help---
+       If you say Y here, you will enable the Unisys uislib driver.
+
diff --git a/drivers/staging/unisys/uislib/Makefile b/drivers/staging/unisys/uislib/Makefile
new file mode 100644 (file)
index 0000000..6e44d49
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# Makefile for Unisys uislib
+#
+
+obj-$(CONFIG_UNISYS_UISLIB)    += visoruislib.o
+
+visoruislib-y := uislib.o uisqueue.o  uisthread.o  uisutils.o
+
+ccflags-y += -Idrivers/staging/unisys/include
+ccflags-y += -Idrivers/staging/unisys/channels
+ccflags-y += -Idrivers/staging/unisys/visorchipset
+ccflags-y += -Idrivers/staging/unisys/sparstopdriver
+ccflags-y += -Idrivers/staging/unisys/common-spar/include
+ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels
+
+ccflags-y += -DCONFIG_SPAR_GUEST -DGUESTDRIVERBUILD -DNOAUTOVERSION
+
diff --git a/drivers/staging/unisys/uislib/uislib.c b/drivers/staging/unisys/uislib/uislib.c
new file mode 100644 (file)
index 0000000..a004c9d
--- /dev/null
@@ -0,0 +1,2536 @@
+/* uislib.c
+ *
+ * Copyright � 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ */
+
+/* @ALL_INSPECTED */
+#define EXPORT_SYMTAB
+#include <linux/kernel.h>
+#include <linux/highmem.h>
+#ifdef CONFIG_MODVERSIONS
+#include <config/modversions.h>
+#endif
+#include <linux/module.h>
+
+#include "commontypes.h"
+
+#include <linux/version.h>
+#include "uniklog.h"
+#include "diagnostics/appos_subsystems.h"
+#include "uisutils.h"
+#include "vbuschannel.h"
+
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>     /* for copy_from_user */
+#include <linux/ctype.h>       /* for toupper */
+#include <linux/list.h>
+
+#include "sparstop.h"
+#include "visorchipset.h"
+#include "chanstub.h"
+#include "version.h"
+#include "guestlinuxdebug.h"
+
+#define SET_PROC_OWNER(x, y)
+
+#define UISLIB_TEST_PROC
+#define POLLJIFFIES_NORMAL 1
+/* Choose whether or not you want to wakeup the request-polling thread
+ * after an IO termination:
+ * this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages
+ */
+#define CURRENT_FILE_PC UISLIB_PC_uislib_c
+#define __MYFILE__ "uislib.c"
+
+/* global function pointers that act as callback functions into virtpcimod */
+int (*VirtControlChanFunc)(struct guest_msgs *);
+
+static int ProcReadBufferValid;
+static char *ProcReadBuffer;   /* Note this MUST be global,
+                                        * because the contents must */
+static unsigned int chipset_inited;
+int callback_count = 0;
+#define WAIT_ON_CALLBACK(handle)       \
+       do {                    \
+               if (handle)             \
+                       break;          \
+               UIS_THREAD_WAIT;        \
+       } while (1)
+
+static struct bus_info *BusListHead;
+static rwlock_t BusListLock;
+static int BusListCount;       /* number of buses in the list */
+static int MaxBusCount;                /* maximum number of buses expected */
+static U64 PhysicalDataChan;
+static int PlatformNumber;
+
+/* This is a list of controlvm messages which could not complete
+ * immediately, but instead must be occasionally retried until they
+ * ultimately succeed/fail.  When this happens,
+ * msg->hdr.Flags.responseExpected determines whether or not we will
+ * send a controlvm response.
+ */
+struct controlvm_retry_entry {
+       CONTROLVM_MESSAGE msg;
+       struct io_msgs cmd;
+       void *obj;
+       int (*controlChanFunc)(struct io_msgs *);
+       struct list_head list_link;
+};
+LIST_HEAD(ControlVmRetryQHead);
+
+static struct uisthread_info Incoming_ThreadInfo;
+static BOOL Incoming_Thread_Started = FALSE;
+LIST_HEAD(List_Polling_Device_Channels);
+unsigned long long tot_moved_to_tail_cnt = 0;
+unsigned long long tot_wait_cnt = 0;
+unsigned long long tot_wakeup_cnt = 0;
+unsigned long long tot_schedule_cnt = 0;
+int en_smart_wakeup = 1;
+static DEFINE_SEMAPHORE(Lock_Polling_Device_Channels); /* unlocked */
+DECLARE_WAIT_QUEUE_HEAD(Wakeup_Polling_Device_Channels);
+static int Go_Polling_Device_Channels;
+
+static struct proc_dir_entry *uislib_proc_dir;
+static struct proc_dir_entry *uislib_proc_vbus_dir;
+static struct proc_dir_entry *vnic_proc_entry; /* Used to be "datachan" */
+static struct proc_dir_entry *ctrlchan_proc_entry;
+static struct proc_dir_entry *pmem_proc_entry;
+static struct proc_dir_entry *info_proc_entry;
+static struct proc_dir_entry *switch_proc_entry;
+static struct proc_dir_entry *extport_proc_entry;
+static struct proc_dir_entry *platformnumber_proc_entry;
+static struct proc_dir_entry *bus_proc_entry;
+static struct proc_dir_entry *dev_proc_entry;
+static struct proc_dir_entry *chipset_proc_entry;
+static struct proc_dir_entry *cycles_before_wait_proc_entry;
+static struct proc_dir_entry *reset_counts_proc_entry;
+static struct proc_dir_entry *smart_wakeup_proc_entry;
+static struct proc_dir_entry *disable_proc_entry;
+
+#define DIR_PROC_ENTRY "uislib"
+#define DIR_VBUS_PROC_ENTRY "vbus"
+#define VNIC_PROC_ENTRY_FN "vnic"      /* Used to be "datachan" */
+#define CTRLCHAN_PROC_ENTRY_FN "ctrlchan"
+#define PMEM_PROC_ENTRY_FN "phys_to_virt"
+#define INFO_PROC_ENTRY_FN "info"
+#define SWITCH_PROC_ENTRY_FN "switch"
+#define SWITCH_COUNT_PROC_ENTRY_FN "switch_count"
+#define EXTPORT_PROC_ENTRY_FN "extport"
+#define PLATFORMNUMBER_PROC_ENTRY_FN "platform"
+#define BUS_PROC_ENTRY_FN "bus"
+#define DEV_PROC_ENTRY_FN "device"
+#define CHIPSET_PROC_ENTRY_FN "chipset"
+#define CYCLES_BEFORE_WAIT_PROC_ENTRY_FN "cycles_before_wait"
+#define RESET_COUNTS_PROC_ENTRY_FN "reset_counts"
+#define SMART_WAKEUP_PROC_ENTRY_FN "smart_wakeup"
+#define CALLHOME_PROC_ENTRY_FN "callhome"
+#define CALLHOME_THROTTLED_PROC_ENTRY_FN "callhome_throttled"
+#define DISABLE_PROC_ENTRY_FN "switch_state"
+#ifdef UISLIB_TEST_PROC
+static struct proc_dir_entry *test_proc_entry;
+#define TEST_PROC_ENTRY_FN "test"
+#endif
+unsigned long long cycles_before_wait, wait_cycles;
+
+/*****************************************************/
+/* local functions                                   */
+/*****************************************************/
+
+static int proc_info_vbus_show(struct seq_file *m, void *v);
+static int
+proc_info_vbus_open(struct inode *inode, struct file *filp)
+{
+       /* proc_info_vbus_show will grab this from seq_file.private: */
+       struct bus_info *bus = PDE_DATA(inode);
+       return single_open(filp, proc_info_vbus_show, bus);
+}
+
+static const struct file_operations proc_info_vbus_fops = {
+       .open = proc_info_vbus_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static ssize_t uislib_proc_read_writeonly(struct file *file,
+                                         char __user *buffer,
+                                         size_t count, loff_t *ppos);
+
+static ssize_t vnic_proc_write(struct file *file, const char __user *buffer,
+                              size_t count, loff_t *ppos);
+
+static const struct file_operations proc_vnic_fops = {
+       .read = uislib_proc_read_writeonly,
+       .write = vnic_proc_write,
+};
+
+static ssize_t chipset_proc_write(struct file *file, const char __user *buffer,
+                                 size_t count, loff_t *ppos);
+
+static const struct file_operations proc_chipset_fops = {
+       .read = uislib_proc_read_writeonly,
+       .write = chipset_proc_write,
+};
+
+static ssize_t info_proc_read(struct file *file, char __user *buf,
+                             size_t len, loff_t *offset);
+static const struct file_operations proc_info_fops = {
+       .read = info_proc_read,
+};
+
+static ssize_t platformnumber_proc_read(struct file *file, char __user *buf,
+                                       size_t len, loff_t *offset);
+static const struct file_operations proc_platformnumber_fops = {
+       .read = platformnumber_proc_read,
+};
+
+static ssize_t cycles_before_wait_proc_write(struct file *file,
+                                            const char __user *buffer,
+                                            size_t count, loff_t *ppos);
+static const struct file_operations proc_cycles_before_wait_fops = {
+       .read = uislib_proc_read_writeonly,
+       .write = cycles_before_wait_proc_write,
+};
+
+static ssize_t reset_counts_proc_write(struct file *file,
+                                      const char __user *buffer,
+                                      size_t count, loff_t *ppos);
+static const struct file_operations proc_reset_counts_fops = {
+       .read = uislib_proc_read_writeonly,
+       .write = reset_counts_proc_write,
+};
+
+static ssize_t smart_wakeup_proc_write(struct file *file,
+                                      const char __user *buffer,
+                                      size_t count, loff_t *ppos);
+static const struct file_operations proc_smart_wakeup_fops = {
+       .read = uislib_proc_read_writeonly,
+       .write = smart_wakeup_proc_write,
+};
+
+static ssize_t test_proc_write(struct file *file,
+                              const char __user *buffer,
+                              size_t count, loff_t *ppos);
+static const struct file_operations proc_test_fops = {
+       .read = uislib_proc_read_writeonly,
+       .write = test_proc_write,
+};
+
+static ssize_t bus_proc_write(struct file *file,
+                             const char __user *buffer,
+                             size_t count, loff_t *ppos);
+static const struct file_operations proc_bus_fops = {
+       .read = uislib_proc_read_writeonly,
+       .write = bus_proc_write,
+};
+
+static ssize_t dev_proc_write(struct file *file,
+                             const char __user *buffer,
+                             size_t count, loff_t *ppos);
+static const struct file_operations proc_dev_fops = {
+       .read = uislib_proc_read_writeonly,
+       .write = dev_proc_write,
+};
+
+static void
+init_msg_header(CONTROLVM_MESSAGE *msg, U32 id, uint rsp, uint svr)
+{
+       memset(msg, 0, sizeof(CONTROLVM_MESSAGE));
+       msg->hdr.Id = id;
+       msg->hdr.Flags.responseExpected = rsp;
+       msg->hdr.Flags.server = svr;
+}
+
+static void
+create_bus_proc_entries(struct bus_info *bus)
+{
+       bus->proc_dir = proc_mkdir(bus->name, uislib_proc_vbus_dir);
+       if (!bus->proc_dir) {
+               LOGERR("failed to create /proc/uislib/vbus/%s directory",
+                      bus->name);
+               return;
+       }
+       bus->proc_info = proc_create_data("info", 0, bus->proc_dir,
+                                         &proc_info_vbus_fops, bus);
+       if (!bus->proc_info) {
+               LOGERR("failed to create /proc/uislib/vbus/%s/info", bus->name);
+               remove_proc_entry(bus->name, uislib_proc_vbus_dir);
+               bus->proc_dir = NULL;
+               return;
+       }
+       SET_PROC_OWNER(bus->proc_info, THIS_MODULE);
+
+}
+
+static void *
+init_vbus_channel(U64 channelAddr, U32 channelBytes, int isServer)
+{
+       void *rc = NULL;
+       void *pChan = uislib_ioremap_cache(channelAddr, channelBytes);
+       if (!pChan) {
+               LOGERR("CONTROLVM_BUS_CREATE error: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed",
+                    (unsigned long long) channelAddr,
+                    (unsigned long long) channelBytes);
+               RETPTR(NULL);
+       }
+       if (isServer) {
+               memset(pChan, 0, channelBytes);
+               if (!ULTRA_VBUS_CHANNEL_OK_SERVER(channelBytes, NULL)) {
+                       ERRDRV("%s channel cannot be used", __func__);
+                       uislib_iounmap(pChan);
+                       RETPTR(NULL);
+               }
+               ULTRA_VBUS_init_channel(pChan, channelBytes);
+       } else {
+               if (!ULTRA_VBUS_CHANNEL_OK_CLIENT(pChan, NULL)) {
+                       ERRDRV("%s channel cannot be used", __func__);
+                       uislib_iounmap(pChan);
+                       RETPTR(NULL);
+               }
+       }
+       RETPTR(pChan);
+Away:
+       return rc;
+}
+
+static int
+create_bus(CONTROLVM_MESSAGE *msg, char *buf)
+{
+       U32 busNo, deviceCount;
+       struct bus_info *tmp, *bus;
+       size_t size;
+
+       if (MaxBusCount == BusListCount) {
+               LOGERR("CONTROLVM_BUS_CREATE Failed: max buses:%d already created\n",
+                    MaxBusCount);
+               POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, MaxBusCount,
+                                POSTCODE_SEVERITY_ERR);
+               return CONTROLVM_RESP_ERROR_MAX_BUSES;
+       }
+
+       busNo = msg->cmd.createBus.busNo;
+       deviceCount = msg->cmd.createBus.deviceCount;
+
+       POSTCODE_LINUX_4(BUS_CREATE_ENTRY_PC, busNo, deviceCount,
+                        POSTCODE_SEVERITY_INFO);
+
+       size =
+           sizeof(struct bus_info) +
+           (deviceCount * sizeof(struct device_info *));
+       bus = UISMALLOC(size, GFP_ATOMIC);
+       if (!bus) {
+               LOGERR("CONTROLVM_BUS_CREATE Failed: kmalloc for bus failed.\n");
+               POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo,
+                                POSTCODE_SEVERITY_ERR);
+               return CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
+       }
+
+       memset(bus, 0, size);
+
+       /* Currently by default, the bus Number is the GuestHandle.
+        * Configure Bus message can override this.
+        */
+       if (msg->hdr.Flags.testMessage) {
+               /* This implies we're the IOVM so set guest handle to 0... */
+               bus->guestHandle = 0;
+               bus->busNo = busNo;
+               bus->localVnic = 1;
+       } else
+               bus->busNo = bus->guestHandle = busNo;
+       sprintf(bus->name, "%d", (int) bus->busNo);
+       bus->deviceCount = deviceCount;
+       bus->device =
+           (struct device_info **) ((char *) bus + sizeof(struct bus_info));
+       bus->busInstGuid = msg->cmd.createBus.busInstGuid;
+       bus->busChannelBytes = 0;
+       bus->pBusChannel = NULL;
+
+       /* add bus to our bus list - but check for duplicates first */
+       read_lock(&BusListLock);
+       for (tmp = BusListHead; tmp; tmp = tmp->next) {
+               if (tmp->busNo == bus->busNo)
+                       break;
+       }
+       read_unlock(&BusListLock);
+       if (tmp) {
+               /* found a bus already in the list with same busNo -
+                * reject add
+                */
+               LOGERR("CONTROLVM_BUS_CREATE Failed: bus %d already exists.\n",
+                      bus->busNo);
+               POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo,
+                                POSTCODE_SEVERITY_ERR);
+               UISFREE(bus, size);
+               return CONTROLVM_RESP_ERROR_ALREADY_DONE;
+       }
+       if ((msg->cmd.createBus.channelAddr != 0)
+           && (msg->cmd.createBus.channelBytes != 0)) {
+               bus->busChannelBytes = msg->cmd.createBus.channelBytes;
+               bus->pBusChannel =
+                   init_vbus_channel(msg->cmd.createBus.channelAddr,
+                                     msg->cmd.createBus.channelBytes,
+                                     msg->hdr.Flags.server);
+       }
+       /* the msg is bound for virtpci; send guest_msgs struct to callback */
+       if (!msg->hdr.Flags.server) {
+               struct guest_msgs cmd;
+               cmd.msgtype = GUEST_ADD_VBUS;
+               cmd.add_vbus.busNo = busNo;
+               cmd.add_vbus.chanptr = bus->pBusChannel;
+               cmd.add_vbus.deviceCount = deviceCount;
+               cmd.add_vbus.busTypeGuid = msg->cmd.createBus.busDataTypeGuid;
+               cmd.add_vbus.busInstGuid = msg->cmd.createBus.busInstGuid;
+               if (!VirtControlChanFunc) {
+                       UISFREE(bus, size);
+                       LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci callback not registered.");
+                       POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo,
+                                        POSTCODE_SEVERITY_ERR);
+                       return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+               }
+               if (!VirtControlChanFunc(&cmd)) {
+                       UISFREE(bus, size);
+                       LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci GUEST_ADD_VBUS returned error.");
+                       POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo,
+                                        POSTCODE_SEVERITY_ERR);
+                       return
+                           CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+               }
+       }
+       create_bus_proc_entries(bus);
+
+       /* add bus at the head of our list */
+       write_lock(&BusListLock);
+       if (!BusListHead)
+               BusListHead = bus;
+       else {
+               bus->next = BusListHead;
+               BusListHead = bus;
+       }
+       BusListCount++;
+       write_unlock(&BusListLock);
+
+       POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus->busNo,
+                        POSTCODE_SEVERITY_INFO);
+       return CONTROLVM_RESP_SUCCESS;
+}
+
+static int
+destroy_bus(CONTROLVM_MESSAGE *msg, char *buf)
+{
+       int i;
+       struct bus_info *bus, *prev = NULL;
+       U32 busNo;
+
+       busNo = msg->cmd.destroyBus.busNo;
+
+       /* find and delete the bus */
+       read_lock(&BusListLock);
+       for (bus = BusListHead; bus; prev = bus, bus = bus->next) {
+               if (bus->busNo == busNo) {
+                       /* found the bus - ensure that all device
+                        * slots are NULL
+                        */
+                       for (i = 0; i < bus->deviceCount; i++) {
+                               if (bus->device[i] != NULL) {
+                                       LOGERR("CONTROLVM_BUS_DESTROY Failed: device %i attached to bus %d.",
+                                            i, busNo);
+                                       read_unlock(&BusListLock);
+                                       return CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED;
+                               }
+                       }
+                       read_unlock(&BusListLock);
+                       /* the msg is bound for virtpci; send
+                        * guest_msgs struct to callback
+                        */
+                       if (!msg->hdr.Flags.server) {
+                               struct guest_msgs cmd;
+                               cmd.msgtype = GUEST_DEL_VBUS;
+                               cmd.del_vbus.busNo = busNo;
+                               if (!VirtControlChanFunc) {
+                                       LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci callback not registered.");
+                                       return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+                               }
+                               if (!VirtControlChanFunc(&cmd)) {
+                                       LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci GUEST_DEL_VBUS returned error.");
+                                       return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+                               }
+                       }
+                       /* remove the bus from the list */
+                       write_lock(&BusListLock);
+                       if (prev)       /* not at head */
+                               prev->next = bus->next;
+                       else
+                               BusListHead = bus->next;
+                       BusListCount--;
+                       write_unlock(&BusListLock);
+                       break;
+               }
+       }
+
+       if (!bus) {
+               LOGERR("CONTROLVM_BUS_DESTROY Failed: failed to find bus %d.\n",
+                      busNo);
+               read_unlock(&BusListLock);
+               return CONTROLVM_RESP_ERROR_ALREADY_DONE;
+       }
+       if (bus->proc_info) {
+               remove_proc_entry("info", bus->proc_dir);
+               bus->proc_info = NULL;
+       }
+       if (bus->proc_dir) {
+               remove_proc_entry(bus->name, uislib_proc_vbus_dir);
+               bus->proc_dir = NULL;
+       }
+       if (bus->pBusChannel) {
+               uislib_iounmap(bus->pBusChannel);
+               bus->pBusChannel = NULL;
+       }
+
+       UISFREE(bus,
+               sizeof(struct bus_info) +
+               (bus->deviceCount * sizeof(struct device_info *)));
+       return CONTROLVM_RESP_SUCCESS;
+}
+
+static int
+create_device(CONTROLVM_MESSAGE *msg, char *buf)
+{
+       struct device_info *dev;
+       struct bus_info *bus;
+       U32 busNo, devNo;
+       int result = CONTROLVM_RESP_SUCCESS;
+       U64 minSize = MIN_IO_CHANNEL_SIZE;
+       ReqHandlerInfo_t *pReqHandler;
+
+       busNo = msg->cmd.createDevice.busNo;
+       devNo = msg->cmd.createDevice.devNo;
+
+       POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, devNo, busNo,
+                        POSTCODE_SEVERITY_INFO);
+
+       dev = UISMALLOC(sizeof(struct device_info), GFP_ATOMIC);
+       if (!dev) {
+               LOGERR("CONTROLVM_DEVICE_CREATE Failed: kmalloc for dev failed.\n");
+               POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo,
+                                POSTCODE_SEVERITY_ERR);
+               return CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
+       }
+
+       memset(dev, 0, sizeof(struct device_info));
+       dev->channelTypeGuid = msg->cmd.createDevice.dataTypeGuid;
+       dev->intr = msg->cmd.createDevice.intr;
+       dev->channelAddr = msg->cmd.createDevice.channelAddr;
+       dev->busNo = busNo;
+       dev->devNo = devNo;
+       sema_init(&dev->interrupt_callback_lock, 1);    /* unlocked */
+       sprintf(dev->devid, "vbus%u:dev%u", (unsigned) busNo, (unsigned) devNo);
+       /* map the channel memory for the device. */
+       if (msg->hdr.Flags.testMessage)
+               dev->chanptr = __va(dev->channelAddr);
+       else {
+               pReqHandler = ReqHandlerFind(dev->channelTypeGuid);
+               if (pReqHandler)
+                       /* generic service handler registered for this
+                        * channel
+                        */
+                       minSize = pReqHandler->min_channel_bytes;
+               if (minSize > msg->cmd.createDevice.channelBytes) {
+                       LOGERR("CONTROLVM_DEVICE_CREATE Failed: channel size is too small, channel size:0x%lx, required size:0x%lx",
+                            (ulong) msg->cmd.createDevice.channelBytes,
+                            (ulong) minSize);
+                       POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo,
+                                        POSTCODE_SEVERITY_ERR);
+                       result = CONTROLVM_RESP_ERROR_CHANNEL_SIZE_TOO_SMALL;
+                       goto Away;
+               }
+               dev->chanptr =
+                   uislib_ioremap_cache(dev->channelAddr,
+                                        msg->cmd.createDevice.channelBytes);
+               if (!dev->chanptr) {
+                       LOGERR("CONTROLVM_DEVICE_CREATE Failed: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed",
+                            dev->channelAddr,
+                            msg->cmd.createDevice.channelBytes);
+                       result = CONTROLVM_RESP_ERROR_IOREMAP_FAILED;
+                       POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo,
+                                        POSTCODE_SEVERITY_ERR);
+                       goto Away;
+               }
+       }
+       dev->devInstGuid = msg->cmd.createDevice.devInstGuid;
+       dev->channelBytes = msg->cmd.createDevice.channelBytes;
+
+       read_lock(&BusListLock);
+       for (bus = BusListHead; bus; bus = bus->next) {
+               if (bus->busNo == busNo) {
+                       /* make sure the device number is valid */
+                       if (devNo >= bus->deviceCount) {
+                               LOGERR("CONTROLVM_DEVICE_CREATE Failed: device (%d) >= deviceCount (%d).",
+                                    devNo, bus->deviceCount);
+                               result = CONTROLVM_RESP_ERROR_MAX_DEVICES;
+                               POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC,
+                                                devNo, busNo,
+                                                POSTCODE_SEVERITY_ERR);
+                               read_unlock(&BusListLock);
+                               goto Away;
+                       }
+                       /* make sure this device is not already set */
+                       if (bus->device[devNo]) {
+                               LOGERR("CONTROLVM_DEVICE_CREATE Failed: device %d is already exists.",
+                                    devNo);
+                               POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC,
+                                                devNo, busNo,
+                                                POSTCODE_SEVERITY_ERR);
+                               result = CONTROLVM_RESP_ERROR_ALREADY_DONE;
+                               read_unlock(&BusListLock);
+                               goto Away;
+                       }
+                       read_unlock(&BusListLock);
+                       /* the msg is bound for virtpci; send
+                        * guest_msgs struct to callback
+                        */
+                       if (!msg->hdr.Flags.server) {
+                               struct guest_msgs cmd;
+                               if (!memcmp
+                                   (&dev->channelTypeGuid,
+                                    &UltraVhbaChannelProtocolGuid,
+                                    sizeof(GUID))) {
+                                       WAIT_FOR_VALID_GUID(((CHANNEL_HEADER
+                                                             *) (dev->
+                                                                 chanptr))->
+                                                           Type);
+                                       if (!ULTRA_VHBA_CHANNEL_OK_CLIENT
+                                           (dev->chanptr, NULL)) {
+                                               LOGERR("CONTROLVM_DEVICE_CREATE Failed:[CLIENT]VHBA dev %d chan invalid.",
+                                                    devNo);
+                                               POSTCODE_LINUX_4
+                                                   (DEVICE_CREATE_FAILURE_PC,
+                                                    devNo, busNo,
+                                                    POSTCODE_SEVERITY_ERR);
+                                               result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID;
+                                               goto Away;
+                                       }
+                                       cmd.msgtype = GUEST_ADD_VHBA;
+                                       cmd.add_vhba.chanptr = dev->chanptr;
+                                       cmd.add_vhba.busNo = busNo;
+                                       cmd.add_vhba.deviceNo = devNo;
+                                       cmd.add_vhba.devInstGuid =
+                                           dev->devInstGuid;
+                                       cmd.add_vhba.intr = dev->intr;
+                               } else
+                                   if (!memcmp
+                                       (&dev->channelTypeGuid,
+                                        &UltraVnicChannelProtocolGuid,
+                                        sizeof(GUID))) {
+                                       WAIT_FOR_VALID_GUID(((CHANNEL_HEADER
+                                                             *) (dev->
+                                                                 chanptr))->
+                                                           Type);
+                                       if (!ULTRA_VNIC_CHANNEL_OK_CLIENT
+                                           (dev->chanptr, NULL)) {
+                                               LOGERR("CONTROLVM_DEVICE_CREATE Failed: VNIC[CLIENT] dev %d chan invalid.",
+                                                    devNo);
+                                               POSTCODE_LINUX_4
+                                                   (DEVICE_CREATE_FAILURE_PC,
+                                                    devNo, busNo,
+                                                    POSTCODE_SEVERITY_ERR);
+                                               result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID;
+                                               goto Away;
+                                       }
+                                       cmd.msgtype = GUEST_ADD_VNIC;
+                                       cmd.add_vnic.chanptr = dev->chanptr;
+                                       cmd.add_vnic.busNo = busNo;
+                                       cmd.add_vnic.deviceNo = devNo;
+                                       cmd.add_vnic.devInstGuid =
+                                           dev->devInstGuid;
+                                       cmd.add_vhba.intr = dev->intr;
+                               } else {
+                                       LOGERR("CONTROLVM_DEVICE_CREATE Failed: unknown channelTypeGuid.\n");
+                                       POSTCODE_LINUX_4
+                                           (DEVICE_CREATE_FAILURE_PC, devNo,
+                                            busNo, POSTCODE_SEVERITY_ERR);
+                                       result = CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
+                                       goto Away;
+                               }
+
+                               if (!VirtControlChanFunc) {
+                                       LOGERR("CONTROLVM_DEVICE_CREATE Failed: virtpci callback not registered.");
+                                       POSTCODE_LINUX_4
+                                           (DEVICE_CREATE_FAILURE_PC, devNo,
+                                            busNo, POSTCODE_SEVERITY_ERR);
+                                       result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+                                       goto Away;
+                               }
+
+                               if (!VirtControlChanFunc(&cmd)) {
+                                       LOGERR("CONTROLVM_DEVICE_CREATE Failed: virtpci GUEST_ADD_[VHBA||VNIC] returned error.");
+                                       POSTCODE_LINUX_4
+                                           (DEVICE_CREATE_FAILURE_PC, devNo,
+                                            busNo, POSTCODE_SEVERITY_ERR);
+                                       result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+                                       goto Away;
+                               }
+                       }
+                       bus->device[devNo] = dev;
+                       POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, devNo, busNo,
+                                        POSTCODE_SEVERITY_INFO);
+                       return CONTROLVM_RESP_SUCCESS;
+               }
+       }
+       read_unlock(&BusListLock);
+
+       LOGERR("CONTROLVM_DEVICE_CREATE Failed: failed to find bus %d.", busNo);
+       POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo,
+                        POSTCODE_SEVERITY_ERR);
+       result = CONTROLVM_RESP_ERROR_BUS_INVALID;
+
+Away:
+       if (!msg->hdr.Flags.testMessage) {
+               uislib_iounmap(dev->chanptr);
+               dev->chanptr = NULL;
+       }
+
+       UISFREE(dev, sizeof(struct device_info));
+       return result;
+}
+
+static int
+pause_device(CONTROLVM_MESSAGE *msg)
+{
+       U32 busNo, devNo;
+       struct bus_info *bus;
+       struct device_info *dev;
+       struct guest_msgs cmd;
+
+       busNo = msg->cmd.deviceChangeState.busNo;
+       devNo = msg->cmd.deviceChangeState.devNo;
+
+       read_lock(&BusListLock);
+       for (bus = BusListHead; bus; bus = bus->next) {
+               if (bus->busNo == busNo) {
+                       /* make sure the device number is valid */
+                       if (devNo >= bus->deviceCount) {
+                               LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device(%d) >= deviceCount(%d).",
+                                    devNo, bus->deviceCount);
+                               read_unlock(&BusListLock);
+                               return CONTROLVM_RESP_ERROR_DEVICE_INVALID;
+                       }
+                       /* make sure this device exists */
+                       dev = bus->device[devNo];
+                       if (!dev) {
+                               LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device %d does not exist.",
+                                    devNo);
+                               read_unlock(&BusListLock);
+                               return CONTROLVM_RESP_ERROR_ALREADY_DONE;
+                       }
+                       read_unlock(&BusListLock);
+                       /* the msg is bound for virtpci; send
+                        * guest_msgs struct to callback
+                        */
+                       if (!memcmp
+                           (&dev->channelTypeGuid,
+                            &UltraVhbaChannelProtocolGuid, sizeof(GUID))) {
+                               cmd.msgtype = GUEST_PAUSE_VHBA;
+                               cmd.pause_vhba.chanptr = dev->chanptr;
+                       } else
+                           if (!memcmp
+                               (&dev->channelTypeGuid,
+                                &UltraVnicChannelProtocolGuid,
+                                sizeof(GUID))) {
+                               cmd.msgtype = GUEST_PAUSE_VNIC;
+                               cmd.pause_vnic.chanptr = dev->chanptr;
+                       } else {
+                               LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: unknown channelTypeGuid.\n");
+                               return
+                                   CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
+                       }
+
+                       if (!VirtControlChanFunc) {
+                               LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered.");
+                               return
+                                   CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+                       }
+
+                       if (!VirtControlChanFunc(&cmd)) {
+                               LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: virtpci GUEST_PAUSE_[VHBA||VNIC] returned error.");
+                               return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+                       }
+                       break;
+               }
+       }
+
+       if (!bus) {
+               LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: bus %d does not exist",
+                    busNo);
+               read_unlock(&BusListLock);
+               return CONTROLVM_RESP_ERROR_BUS_INVALID;
+       }
+
+       return CONTROLVM_RESP_SUCCESS;
+}
+
+static int
+resume_device(CONTROLVM_MESSAGE *msg)
+{
+       U32 busNo, devNo;
+       struct bus_info *bus;
+       struct device_info *dev;
+       struct guest_msgs cmd;
+
+       busNo = msg->cmd.deviceChangeState.busNo;
+       devNo = msg->cmd.deviceChangeState.devNo;
+
+       read_lock(&BusListLock);
+       for (bus = BusListHead; bus; bus = bus->next) {
+               if (bus->busNo == busNo) {
+                       /* make sure the device number is valid */
+                       if (devNo >= bus->deviceCount) {
+                               LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device(%d) >= deviceCount(%d).",
+                                    devNo, bus->deviceCount);
+                               read_unlock(&BusListLock);
+                               return CONTROLVM_RESP_ERROR_DEVICE_INVALID;
+                       }
+                       /* make sure this device exists */
+                       dev = bus->device[devNo];
+                       if (!dev) {
+                               LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device %d does not exist.",
+                                    devNo);
+                               read_unlock(&BusListLock);
+                               return CONTROLVM_RESP_ERROR_ALREADY_DONE;
+                       }
+                       read_unlock(&BusListLock);
+                       /* the msg is bound for virtpci; send
+                        * guest_msgs struct to callback
+                        */
+                       if (!memcmp(&dev->channelTypeGuid,
+                                   &UltraVhbaChannelProtocolGuid,
+                                   sizeof(GUID))) {
+                               cmd.msgtype = GUEST_RESUME_VHBA;
+                               cmd.resume_vhba.chanptr = dev->chanptr;
+                       } else
+                           if (!memcmp(&dev->channelTypeGuid,
+                                       &UltraVnicChannelProtocolGuid,
+                                       sizeof(GUID))) {
+                               cmd.msgtype = GUEST_RESUME_VNIC;
+                               cmd.resume_vnic.chanptr = dev->chanptr;
+                       } else {
+                               LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: unknown channelTypeGuid.\n");
+                               return
+                                   CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
+                       }
+
+                       if (!VirtControlChanFunc) {
+                               LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered.");
+                               return
+                                   CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+                       }
+
+                       if (!VirtControlChanFunc(&cmd)) {
+                               LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: virtpci GUEST_RESUME_[VHBA||VNIC] returned error.");
+                               return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+                       }
+                       break;
+               }
+       }
+
+       if (!bus) {
+               LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: bus %d does not exist",
+                    busNo);
+               read_unlock(&BusListLock);
+               return CONTROLVM_RESP_ERROR_BUS_INVALID;
+       }
+
+       return CONTROLVM_RESP_SUCCESS;
+}
+
+static int
+destroy_device(CONTROLVM_MESSAGE *msg, char *buf)
+{
+       U32 busNo, devNo;
+       struct bus_info *bus;
+       struct device_info *dev;
+       struct guest_msgs cmd;
+
+       busNo = msg->cmd.destroyDevice.busNo;
+       devNo = msg->cmd.destroyDevice.devNo;
+
+       read_lock(&BusListLock);
+       LOGINF("destroy_device called for busNo=%u, devNo=%u", busNo, devNo);
+       for (bus = BusListHead; bus; bus = bus->next) {
+               if (bus->busNo == busNo) {
+                       /* make sure the device number is valid */
+                       if (devNo >= bus->deviceCount) {
+                               LOGERR("CONTROLVM_DEVICE_DESTORY Failed: device(%d) >= deviceCount(%d).",
+                                    devNo, bus->deviceCount);
+                               read_unlock(&BusListLock);
+                               return CONTROLVM_RESP_ERROR_DEVICE_INVALID;
+                       }
+                       /* make sure this device exists */
+                       dev = bus->device[devNo];
+                       if (!dev) {
+                               LOGERR("CONTROLVM_DEVICE_DESTROY Failed: device %d does not exist.",
+                                    devNo);
+                               read_unlock(&BusListLock);
+                               return CONTROLVM_RESP_ERROR_ALREADY_DONE;
+                       }
+                       read_unlock(&BusListLock);
+                       /* the msg is bound for virtpci; send
+                        * guest_msgs struct to callback
+                        */
+                       if (!memcmp
+                           (&dev->channelTypeGuid,
+                            &UltraVhbaChannelProtocolGuid, sizeof(GUID))) {
+                               cmd.msgtype = GUEST_DEL_VHBA;
+                               cmd.del_vhba.chanptr = dev->chanptr;
+                       } else
+                           if (!memcmp
+                               (&dev->channelTypeGuid,
+                                &UltraVnicChannelProtocolGuid,
+                                sizeof(GUID))) {
+                               cmd.msgtype = GUEST_DEL_VNIC;
+                               cmd.del_vnic.chanptr = dev->chanptr;
+                       } else {
+                               LOGERR("CONTROLVM_DEVICE_DESTROY Failed: unknown channelTypeGuid.\n");
+                               return
+                                   CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
+                       }
+
+                       if (!VirtControlChanFunc) {
+                               LOGERR("CONTROLVM_DEVICE_DESTORY Failed: virtpci callback not registered.");
+                               return
+                                   CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+                       }
+
+                       if (!VirtControlChanFunc(&cmd)) {
+                               LOGERR("CONTROLVM_DEVICE_DESTROY Failed: virtpci GUEST_DEL_[VHBA||VNIC] returned error.");
+                               return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+                       }
+/* you must disable channel interrupts BEFORE you unmap the channel,
+ * because if you unmap first, there may still be some activity going
+ * on which accesses the channel and you will get a "unable to handle
+ * kernel paging request"
+ */
+                       if (dev->polling) {
+                               LOGINF("calling uislib_disable_channel_interrupts");
+                               uislib_disable_channel_interrupts(busNo, devNo);
+                       }
+                       /* unmap the channel memory for the device. */
+                       if (!msg->hdr.Flags.testMessage) {
+                               LOGINF("destroy_device, doing iounmap");
+                               uislib_iounmap(dev->chanptr);
+                       }
+                       UISFREE(dev, sizeof(struct device_info));
+                       bus->device[devNo] = NULL;
+                       break;
+               }
+       }
+
+       if (!bus) {
+               LOGERR("CONTROLVM_DEVICE_DESTROY Failed: bus %d does not exist",
+                      busNo);
+               read_unlock(&BusListLock);
+               return CONTROLVM_RESP_ERROR_BUS_INVALID;
+       }
+
+       return CONTROLVM_RESP_SUCCESS;
+}
+
+void
+ULTRA_disp_channel_header(CHANNEL_HEADER *x)
+{
+       LOGINF("Sig=%llx, HdrSz=%lx, Sz=%llx, Feat=%llx, hPart=%llx, Hndl=%llx, ChSpace=%llx, Ver=%lx, PartIdx=%lx\n",
+            x->Signature, (long unsigned int) x->HeaderSize, x->Size,
+            x->Features, x->PartitionHandle, x->Handle, x->oChannelSpace,
+            (long unsigned int) x->VersionId,
+            (long unsigned int) x->PartitionIndex);
+
+       LOGINF("ClientStr=%lx, CliStBoot=%lx, CmdStCli=%lx, CliStOS=%lx, ChCharistics=%lx, CmdStSrv=%lx, SrvSt=%lx\n",
+            (long unsigned int) x->oClientString,
+            (long unsigned int) x->CliStateBoot,
+            (long unsigned int) x->CmdStateCli,
+            (long unsigned int) x->CliStateOS,
+            (long unsigned int) x->ChannelCharacteristics,
+            (long unsigned int) x->CmdStateSrv,
+            (long unsigned int) x->SrvState);
+
+}
+
+void
+ULTRA_disp_channel(ULTRA_IO_CHANNEL_PROTOCOL *x)
+{
+       ULTRA_disp_channel_header(&x->ChannelHeader);
+       LOGINF("cmdQ.Type=%lx\n", (long unsigned int) x->cmdQ.Type);
+       LOGINF("cmdQ.Size=%llx\n", x->cmdQ.Size);
+       LOGINF("cmdQ.oSignalBase=%llx\n", x->cmdQ.oSignalBase);
+       LOGINF("cmdQ.SignalSize=%lx\n", (long unsigned int) x->cmdQ.SignalSize);
+       LOGINF("cmdQ.MaxSignalSlots=%lx\n",
+              (long unsigned int) x->cmdQ.MaxSignalSlots);
+       LOGINF("cmdQ.MaxSignals=%lx\n", (long unsigned int) x->cmdQ.MaxSignals);
+       LOGINF("rspQ.Type=%lx\n", (long unsigned int) x->rspQ.Type);
+       LOGINF("rspQ.Size=%llx\n", x->rspQ.Size);
+       LOGINF("rspQ.oSignalBase=%llx\n", x->rspQ.oSignalBase);
+       LOGINF("rspQ.SignalSize=%lx\n", (long unsigned int) x->rspQ.SignalSize);
+       LOGINF("rspQ.MaxSignalSlots=%lx\n",
+              (long unsigned int) x->rspQ.MaxSignalSlots);
+       LOGINF("rspQ.MaxSignals=%lx\n", (long unsigned int) x->rspQ.MaxSignals);
+       LOGINF("SIZEOF_CMDRSP=%lx\n", SIZEOF_CMDRSP);
+       LOGINF("SIZEOF_PROTOCOL=%lx\n", SIZEOF_PROTOCOL);
+}
+
+void
+ULTRA_disp_vnic_channel(ULTRA_IO_CHANNEL_PROTOCOL *x)
+{
+       LOGINF("num_rcv_bufs=%lx\n", (long unsigned int) x->vnic.num_rcv_bufs);
+       LOGINF("mtu=%lx\n", (long unsigned int) x->vnic.mtu);
+}
+
+static int
+init_chipset(CONTROLVM_MESSAGE *msg, char *buf)
+{
+       POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+
+       MaxBusCount = msg->cmd.initChipset.busCount;
+       PlatformNumber = msg->cmd.initChipset.platformNumber;
+       PhysicalDataChan = 0;
+
+       /* We need to make sure we have our functions registered
+       * before processing messages.  If we are a test vehicle the
+       * testMessage for init_chipset will be set.  We can ignore the
+       * waits for the callbacks, since this will be manually entered
+       * from a user.  If no testMessage is set, we will wait for the
+       * functions.
+       */
+       if (!msg->hdr.Flags.testMessage)
+               WAIT_ON_CALLBACK(VirtControlChanFunc);
+
+       chipset_inited = 1;
+       POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO);
+
+       return CONTROLVM_RESP_SUCCESS;
+}
+
+static int
+stop_chipset(CONTROLVM_MESSAGE *msg, char *buf)
+{
+       /* Check that all buses and switches have been torn down and
+        * destroyed.
+        */
+       if (BusListHead) {
+               /* Buses still exist. */
+               LOGERR("CONTROLVM_CHIPSET_STOP: BusListHead is not NULL");
+               return CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_BUS;
+       }
+       if (BusListCount) {
+               /* BusListHead is NULL, but BusListCount != 0 */
+               LOGERR("CONTROLVM_CHIPSET_STOP: BusListCount != 0");
+               return CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_BUS;
+       }
+
+       /* Buses are shut down. */
+       return visorchipset_chipset_notready();
+}
+
+static int
+delete_bus_glue(U32 busNo)
+{
+       CONTROLVM_MESSAGE msg;
+
+       init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0);
+       msg.cmd.destroyBus.busNo = busNo;
+       if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("destroy_bus failed. busNo=0x%x\n", busNo);
+               return 0;
+       }
+       return 1;
+}
+
+static int
+delete_device_glue(U32 busNo, U32 devNo)
+{
+       CONTROLVM_MESSAGE msg;
+
+       init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0);
+       msg.cmd.destroyDevice.busNo = busNo;
+       msg.cmd.destroyDevice.devNo = devNo;
+       if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("destroy_device failed. busNo=0x%x devNo=0x%x\n", busNo,
+                      devNo);
+               return 0;
+       }
+       return 1;
+}
+
+int
+uislib_client_inject_add_bus(U32 busNo, GUID instGuid,
+                            U64 channelAddr, ulong nChannelBytes)
+{
+       CONTROLVM_MESSAGE msg;
+
+       LOGINF("enter busNo=0x%x\n", busNo);
+       /* step 0: init the chipset */
+       POSTCODE_LINUX_3(CHIPSET_INIT_ENTRY_PC, busNo, POSTCODE_SEVERITY_INFO);
+
+       if (!chipset_inited) {
+               /* step: initialize the chipset */
+               init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0);
+               /* this change is needed so that console will come up
+               * OK even when the bus 0 create comes in late.  If the
+               * bus 0 create is the first create, then the add_vnic
+               * will work fine, but if the bus 0 create arrives
+               * after number 4, then the add_vnic will fail, and the
+               * ultraboot will fail.
+               */
+               msg.cmd.initChipset.busCount = 23;
+               msg.cmd.initChipset.switchCount = 0;
+               if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+                       LOGERR("init_chipset failed.\n");
+                       return 0;
+               }
+               LOGINF("chipset initialized\n");
+               POSTCODE_LINUX_3(CHIPSET_INIT_EXIT_PC, busNo,
+                                POSTCODE_SEVERITY_INFO);
+       }
+
+       /* step 1: create a bus */
+       POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, busNo, POSTCODE_SEVERITY_WARNING);
+       init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0);
+       msg.cmd.createBus.busNo = busNo;
+       msg.cmd.createBus.deviceCount = 23;     /* devNo+1; */
+       msg.cmd.createBus.channelAddr = channelAddr;
+       msg.cmd.createBus.channelBytes = nChannelBytes;
+       if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("create_bus failed.\n");
+               POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo,
+                                POSTCODE_SEVERITY_ERR);
+               return 0;
+       }
+       POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, busNo, POSTCODE_SEVERITY_INFO);
+
+       return 1;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_add_bus);
+
+
+int
+uislib_client_inject_del_bus(U32 busNo)
+{
+       return delete_bus_glue(busNo);
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_del_bus);
+
+int
+uislib_client_inject_pause_vhba(U32 busNo, U32 devNo)
+{
+       CONTROLVM_MESSAGE msg;
+       int rc;
+
+       init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
+       msg.cmd.deviceChangeState.busNo = busNo;
+       msg.cmd.deviceChangeState.devNo = devNo;
+       msg.cmd.deviceChangeState.state = SegmentStateStandby;
+       rc = pause_device(&msg);
+       if (rc != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("VHBA pause_device failed. busNo=0x%x devNo=0x%x\n",
+                      busNo, devNo);
+               return rc;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vhba);
+
+int
+uislib_client_inject_resume_vhba(U32 busNo, U32 devNo)
+{
+       CONTROLVM_MESSAGE msg;
+       int rc;
+
+       init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
+       msg.cmd.deviceChangeState.busNo = busNo;
+       msg.cmd.deviceChangeState.devNo = devNo;
+       msg.cmd.deviceChangeState.state = SegmentStateRunning;
+       rc = resume_device(&msg);
+       if (rc != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("VHBA resume_device failed. busNo=0x%x devNo=0x%x\n",
+                      busNo, devNo);
+               return rc;
+       }
+       return 0;
+
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vhba);
+
+int
+uislib_client_inject_add_vhba(U32 busNo, U32 devNo,
+                             U64 phys_chan_addr, U32 chan_bytes,
+                             int is_test_addr, GUID instGuid,
+                             struct InterruptInfo *intr)
+{
+       CONTROLVM_MESSAGE msg;
+
+       LOGINF(" enter busNo=0x%x devNo=0x%x\n", busNo, devNo);
+       /* chipset init'ed with bus bus has been previously created -
+       * Verify it still exists step 2: create the VHBA device on the
+       * bus
+       */
+       POSTCODE_LINUX_4(VHBA_CREATE_ENTRY_PC, devNo, busNo,
+                        POSTCODE_SEVERITY_INFO);
+
+       init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0);
+       if (is_test_addr)
+               /* signify that the physical channel address does NOT
+                * need to be ioremap()ed
+                */
+               msg.hdr.Flags.testMessage = 1;
+       msg.cmd.createDevice.busNo = busNo;
+       msg.cmd.createDevice.devNo = devNo;
+       msg.cmd.createDevice.devInstGuid = instGuid;
+       if (intr)
+               msg.cmd.createDevice.intr = *intr;
+       else
+               memset(&msg.cmd.createDevice.intr, 0,
+                      sizeof(struct InterruptInfo));
+       msg.cmd.createDevice.channelAddr = phys_chan_addr;
+       if (chan_bytes < MIN_IO_CHANNEL_SIZE) {
+               LOGERR("wrong channel size.chan_bytes = 0x%x IO_CHANNEL_SIZE= 0x%x\n",
+                    chan_bytes, (unsigned int) MIN_IO_CHANNEL_SIZE);
+               POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, chan_bytes,
+                                MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR);
+               return 0;
+       }
+       msg.cmd.createDevice.channelBytes = chan_bytes;
+       msg.cmd.createDevice.dataTypeGuid = UltraVhbaChannelProtocolGuid;
+       if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("VHBA create_device failed.\n");
+               POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, devNo, busNo,
+                                POSTCODE_SEVERITY_ERR);
+               return 0;
+       }
+       POSTCODE_LINUX_4(VHBA_CREATE_SUCCESS_PC, devNo, busNo,
+                        POSTCODE_SEVERITY_INFO);
+       return 1;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_add_vhba);
+
+int
+uislib_client_inject_del_vhba(U32 busNo, U32 devNo)
+{
+       return delete_device_glue(busNo, devNo);
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_del_vhba);
+
+int
+uislib_client_inject_add_vnic(U32 busNo, U32 devNo,
+                             U64 phys_chan_addr, U32 chan_bytes,
+                             int is_test_addr, GUID instGuid,
+                             struct InterruptInfo *intr)
+{
+       CONTROLVM_MESSAGE msg;
+
+       LOGINF(" enter busNo=0x%x devNo=0x%x\n", busNo, devNo);
+       /* chipset init'ed with bus bus has been previously created -
+       * Verify it still exists step 2: create the VNIC device on the
+       * bus
+       */
+       POSTCODE_LINUX_4(VNIC_CREATE_ENTRY_PC, devNo, busNo,
+                        POSTCODE_SEVERITY_INFO);
+
+       init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0);
+       if (is_test_addr)
+               /* signify that the physical channel address does NOT
+                * need to be ioremap()ed
+                */
+               msg.hdr.Flags.testMessage = 1;
+       msg.cmd.createDevice.busNo = busNo;
+       msg.cmd.createDevice.devNo = devNo;
+       msg.cmd.createDevice.devInstGuid = instGuid;
+       if (intr)
+               msg.cmd.createDevice.intr = *intr;
+       else
+               memset(&msg.cmd.createDevice.intr, 0,
+                      sizeof(struct InterruptInfo));
+       msg.cmd.createDevice.channelAddr = phys_chan_addr;
+       if (chan_bytes < MIN_IO_CHANNEL_SIZE) {
+               LOGERR("wrong channel size.chan_bytes = 0x%x IO_CHANNEL_SIZE= 0x%x\n",
+                    chan_bytes, (unsigned int) MIN_IO_CHANNEL_SIZE);
+               POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, chan_bytes,
+                                MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR);
+               return 0;
+       }
+       msg.cmd.createDevice.channelBytes = chan_bytes;
+       msg.cmd.createDevice.dataTypeGuid = UltraVnicChannelProtocolGuid;
+       if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("VNIC create_device failed.\n");
+               POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, devNo, busNo,
+                                POSTCODE_SEVERITY_ERR);
+               return 0;
+       }
+
+       POSTCODE_LINUX_4(VNIC_CREATE_SUCCESS_PC, devNo, busNo,
+                        POSTCODE_SEVERITY_INFO);
+       return 1;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_add_vnic);
+
+int
+uislib_client_inject_pause_vnic(U32 busNo, U32 devNo)
+{
+       CONTROLVM_MESSAGE msg;
+       int rc;
+
+       init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
+       msg.cmd.deviceChangeState.busNo = busNo;
+       msg.cmd.deviceChangeState.devNo = devNo;
+       msg.cmd.deviceChangeState.state = SegmentStateStandby;
+       rc = pause_device(&msg);
+       if (rc != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("VNIC pause_device failed. busNo=0x%x devNo=0x%x\n",
+                      busNo, devNo);
+               return -1;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vnic);
+
+int
+uislib_client_inject_resume_vnic(U32 busNo, U32 devNo)
+{
+       CONTROLVM_MESSAGE msg;
+       int rc;
+
+       init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
+       msg.cmd.deviceChangeState.busNo = busNo;
+       msg.cmd.deviceChangeState.devNo = devNo;
+       msg.cmd.deviceChangeState.state = SegmentStateRunning;
+       rc = resume_device(&msg);
+       if (rc != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("VNIC resume_device failed. busNo=0x%x devNo=0x%x\n",
+                      busNo, devNo);
+               return -1;
+       }
+       return 0;
+
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vnic);
+
+int
+uislib_client_inject_del_vnic(U32 busNo, U32 devNo)
+{
+       return delete_device_glue(busNo, devNo);
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_del_vnic);
+
+int
+uislib_client_add_vnic(U32 busNo)
+{
+       BOOL busCreated = FALSE;
+       int devNo = 0;          /* Default to 0, since only one device
+                                * will be created for this bus... */
+       GUID dummyGuid = GUID0;
+       CONTROLVM_MESSAGE msg;
+
+       init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0);
+       msg.hdr.Flags.testMessage = 1;
+       msg.cmd.createBus.busNo = busNo;
+       msg.cmd.createBus.deviceCount = 4;
+       msg.cmd.createBus.channelAddr = 0;
+       msg.cmd.createBus.channelBytes = 0;
+       if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("client create_bus failed");
+               return 0;
+       }
+       busCreated = TRUE;
+
+       init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0);
+       msg.hdr.Flags.testMessage = 1;
+       msg.cmd.createDevice.busNo = busNo;
+       msg.cmd.createDevice.devNo = devNo;
+       msg.cmd.createDevice.devInstGuid = dummyGuid;
+       memset(&msg.cmd.createDevice.intr, 0, sizeof(struct InterruptInfo));
+       msg.cmd.createDevice.channelAddr = PhysicalDataChan;
+       msg.cmd.createDevice.channelBytes = MIN_IO_CHANNEL_SIZE;
+       msg.cmd.createDevice.dataTypeGuid = UltraVnicChannelProtocolGuid;
+       if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+               LOGERR("client create_device failed");
+               goto AwayCleanup;
+       }
+
+       return 1;
+
+AwayCleanup:
+       if (busCreated) {
+               init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0);
+               msg.hdr.Flags.testMessage = 1;
+               msg.cmd.destroyBus.busNo = busNo;
+               if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS)
+                       LOGERR("client destroy_bus failed.\n");
+       }
+
+       return 0;
+}                              /* end uislib_client_add_vnic */
+EXPORT_SYMBOL_GPL(uislib_client_add_vnic);
+
+int
+uislib_client_delete_vnic(U32 busNo)
+{
+       int devNo = 0;          /* Default to 0, since only one device
+                                * will be created for this bus... */
+       CONTROLVM_MESSAGE msg;
+
+       init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0);
+       msg.hdr.Flags.testMessage = 1;
+       msg.cmd.destroyDevice.busNo = busNo;
+       msg.cmd.destroyDevice.devNo = devNo;
+       if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+               /* Don't error exit - try to see if bus can be destroyed... */
+               LOGERR("client destroy_device failed.\n");
+       }
+
+       init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0);
+       msg.hdr.Flags.testMessage = 1;
+       msg.cmd.destroyBus.busNo = busNo;
+       if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS)
+               LOGERR("client destroy_bus failed.\n");
+
+       return 1;
+}
+EXPORT_SYMBOL_GPL(uislib_client_delete_vnic);
+
+                               /* end client_delete_vnic */
+
+static atomic_t Malloc_BytesInUse = ATOMIC_INIT(0);
+static atomic_t Malloc_BuffersInUse = ATOMIC_INIT(0);
+static atomic_t Malloc_FailuresAlloc = ATOMIC_INIT(0);
+static atomic_t Malloc_FailuresFree = ATOMIC_INIT(0);
+static atomic_t Malloc_TotalMallocs = ATOMIC_INIT(0);
+static atomic_t Malloc_TotalFrees = ATOMIC_INIT(0);
+
+void *
+uislib_malloc(size_t siz, gfp_t gfp, U8 contiguous, char *fn, int ln)
+{
+       void *p = NULL;
+
+       if (contiguous == 0) {
+               /* Allocate non-contiguous memory, such as in the
+               * add_vnic and add_vhba methods where we are rebooting
+               * the guest, for example.  Otherwise the contiguous
+               * memory allocation attempt results in an
+               * out-of-memory crash in the IOVM...
+               */
+               p = vmalloc(siz);
+       } else {
+               /* __GFP_NORETRY means "ok to fail", meaning kmalloc()
+               * can return NULL.  If you do NOT specify
+               * __GFP_NORETRY, Linux will go to extreme measures to
+               * get memory for you (like, invoke oom killer), which
+               * will probably cripple the system.
+               */
+               p = kmalloc(siz, gfp | __GFP_NORETRY);
+       }
+       if (p == NULL) {
+               LOGERR("uislib_malloc failed to alloc %d bytes @%s:%d",
+                      (int) siz, fn, ln);
+               atomic_inc(&Malloc_FailuresAlloc);
+               return NULL;
+       }
+       atomic_add((int) (siz), &Malloc_BytesInUse);
+       atomic_inc(&Malloc_BuffersInUse);
+       atomic_inc(&Malloc_TotalMallocs);       /* will eventually overflow */
+       return p;
+}
+EXPORT_SYMBOL_GPL(uislib_malloc);
+
+void
+uislib_free(void *p, size_t siz, U8 contiguous, char *fn, int ln)
+{
+       if (p == NULL) {
+               LOGERR("uislib_free NULL pointer @%s:%d", fn, ln);
+               atomic_inc(&Malloc_FailuresFree);
+               return;
+       }
+
+       if (contiguous == 0)
+               vfree(p);
+       else
+               kfree(p);
+       atomic_sub((int) (siz), &Malloc_BytesInUse);
+       atomic_dec(&Malloc_BuffersInUse);
+       atomic_inc(&Malloc_TotalFrees); /* will eventually overflow */
+}
+EXPORT_SYMBOL_GPL(uislib_free);
+
+void *
+uislib_cache_alloc(struct kmem_cache *cur_pool, char *fn, int ln)
+{
+       /* __GFP_NORETRY means "ok to fail", meaning kmalloc() can
+       * return NULL.  If you do NOT specify __GFP_NORETRY, Linux
+       * will go to extreme measures to get memory for you (like,
+       * invoke oom killer), which will probably cripple the system.
+       */
+       void *p = kmem_cache_alloc(cur_pool, GFP_ATOMIC | __GFP_NORETRY);
+       if (p == NULL) {
+               LOGERR("uislib_malloc failed to alloc uiscmdrsp @%s:%d",
+                      fn, ln);
+               return NULL;
+       }
+       return p;
+}
+EXPORT_SYMBOL_GPL(uislib_cache_alloc);
+
+void
+uislib_cache_free(struct kmem_cache *cur_pool, void *p, char *fn, int ln)
+{
+       if (p == NULL) {
+               LOGERR("uislib_free NULL pointer @%s:%d", fn, ln);
+               return;
+       }
+       kmem_cache_free(cur_pool, p);
+}
+EXPORT_SYMBOL_GPL(uislib_cache_free);
+
+/*****************************************************/
+/* proc filesystem callback functions                */
+/*****************************************************/
+
+static ssize_t
+vnic_proc_write(struct file *file, const char __user *buffer,
+               size_t count, loff_t *ppos)
+{
+       int action = 0xffff, busNo = 0, i, result = 0;
+       char buf[count];
+       char direction;
+/* GUID guid; */
+       if (copy_from_user(buf, buffer, count)) {
+               LOGERR("echo > /proc/uislib/vnic copy_from_user ****FAILED.\n");
+               return -EFAULT;
+       }
+
+       i = sscanf(buf, "%d%c", &action, &direction);
+       if (i != 2) {
+               LOGERR("unable to parse vnic proc parameters.\n");
+               return -EFAULT;
+       }
+
+       if ((direction != '-') && (direction != '+')) {
+               LOGERR("unable to determine whether to add or delete vnic\n");
+               return -EFAULT;
+       }
+
+       /* if (i < 1), i.e., if we didn't even read the action field,
+       * then action will default to 0xffff and the code below will
+       * fall through the switch and print usage.
+       */
+       switch (action) {
+       case 0:
+               /* call client method... */
+               busNo = 0;      /* All client drivers use bus value of 0... */
+               if (direction == '+')
+                       result = uislib_client_add_vnic(busNo);
+               else
+                       result = uislib_client_delete_vnic(busNo);
+               if (!result) {
+                       LOGERR("echo 0%c > /proc/uislib/vnic failed (client end)",
+                            direction);
+                       return -EFAULT;
+               }
+               return count;
+
+       default:
+               break;
+       }
+
+       LOGERR("USAGE: echo <action><direction (up/down)> > /proc/uislib/vnic");
+       LOGERR(" ");
+       LOGERR("Client Syntax");
+       LOGERR("-------------");
+       LOGERR("0+    ==> add vnic");
+       LOGERR("0-    ==> delete vnic");
+       LOGERR(" ");
+       return count;
+}                              /* end vnic_proc_write */
+
+static ssize_t
+chipset_proc_write(struct file *file, const char __user *buffer,
+                  size_t count, loff_t *ppos)
+{
+       int i, action = 0xffff;
+       char buf[count];
+       CONTROLVM_MESSAGE msg;
+
+       memset(&msg, 0, sizeof(CONTROLVM_MESSAGE));
+
+       if (copy_from_user(buf, buffer, count)) {
+               LOGERR("copy_from_user ****FAILED.\n");
+               return -EFAULT;
+       }
+
+       if (chipset_inited) {
+               LOGINF("Chipset already initialized\n");
+               return -EFAULT;
+       }
+       i = sscanf(buf, "%x", &action);
+
+       /* if (i < 1), i.e., if we didn't even read the action field,
+       * then action will default to 0xffff and the code below will
+       * fall through the switch and print usage.
+       */
+       switch (action) {
+       case 1:
+               /* GUEST */
+               /* step: initialize the chipset */
+               init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0);
+               msg.hdr.Flags.testMessage = 0;
+               msg.cmd.initChipset.busCount = 23;
+               msg.cmd.initChipset.switchCount = 23;
+
+               if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+                       LOGERR("init_chipset failed.\n");
+                       return 0;
+               }
+               return 1;
+       case 2:
+               /* BOTH */
+               init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0);
+               msg.hdr.Flags.testMessage = 1;
+               msg.cmd.initChipset.busCount = 23;
+               msg.cmd.initChipset.switchCount = 23;
+
+               if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+                       LOGERR("init_chipset failed.\n");
+                       return 0;
+               }
+               return 1;
+
+       default:
+               break;
+       }
+
+       LOGERR("usage: 1 ==> init_chipset client\n");
+       LOGERR("usage: 2 ==> init_chipset test\n");
+       return -EFAULT;
+}
+
+#define PROCLINE(...)                                                  \
+       do {                                                            \
+               if (util_add_proc_line_ex(&tot, buff,                   \
+                                         buff_len, __VA_ARGS__) < 0) { \
+                       goto err_done;                                  \
+               }                                                       \
+       } while (0)
+
+static int
+info_proc_read_helper(char **buff, int *buff_len)
+{
+       int i, tot = 0;
+       struct bus_info *bus;
+
+       PROCLINE("\nBuses:\n");
+
+       read_lock(&BusListLock);
+       for (bus = BusListHead; bus; bus = bus->next) {
+
+               PROCLINE("    bus=0x%p, busNo=%d, deviceCount=%d\n",
+                        bus, bus->busNo, bus->deviceCount);
+
+               PROCLINE("        Devices:\n");
+
+               for (i = 0; i < bus->deviceCount; i++) {
+                       if (bus->device[i]) {
+                               PROCLINE("            busNo %d, device[%i]: 0x%p, chanptr=0x%p, swtch=0x%p\n",
+                                    bus->busNo, i, bus->device[i],
+                                    bus->device[i]->chanptr,
+                                    bus->device[i]->swtch);
+                               PROCLINE("            first_busy_cnt=%llu, moved_to_tail_cnt=%llu, last_on_list_cnt=%llu\n",
+                                    bus->device[i]->first_busy_cnt,
+                                    bus->device[i]->moved_to_tail_cnt,
+                                    bus->device[i]->last_on_list_cnt);
+                       }
+               }
+       }
+       read_unlock(&BusListLock);
+
+       PROCLINE("Malloc bytes in use: %d\n", atomic_read(&Malloc_BytesInUse));
+       PROCLINE("Malloc buffers in use: %d\n",
+                atomic_read(&Malloc_BuffersInUse));
+       PROCLINE("Malloc allocation failures: %d\n",
+                atomic_read(&Malloc_FailuresAlloc));
+       PROCLINE("Malloc free failures: %d\n",
+                atomic_read(&Malloc_FailuresFree));
+       PROCLINE("Malloc total mallocs: %u (may overflow)\n",
+                (unsigned) atomic_read(&Malloc_TotalMallocs));
+       PROCLINE("Malloc total frees: %u (may overflow)\n",
+                (unsigned) atomic_read(&Malloc_TotalFrees));
+       PROCLINE("UisUtils_Registered_Services: %d\n",
+                atomic_read(&UisUtils_Registered_Services));
+
+       PROCLINE("cycles_before_wait %llu wait_cycles:%llu\n",
+                cycles_before_wait, wait_cycles);
+       PROCLINE("tot_wakeup_cnt %llu:tot_wait_cnt %llu:tot_schedule_cnt %llu\n",
+            tot_wakeup_cnt, tot_wait_cnt, tot_schedule_cnt);
+       PROCLINE("en_smart_wakeup %d\n", en_smart_wakeup);
+       PROCLINE("tot_moved_to_tail_cnt %llu\n", tot_moved_to_tail_cnt);
+
+       return tot;
+err_done:
+
+       return -1;
+}
+
+static ssize_t
+info_proc_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
+{
+       char *temp;
+       int totalBytes = 0;
+       int remaining_bytes = PROC_READ_BUFFER_SIZE;
+
+/* *start = buf; */
+       if (ProcReadBuffer == NULL) {
+               DBGINF("ProcReadBuffer == NULL; allocating buffer.\n.");
+               ProcReadBuffer = UISVMALLOC(PROC_READ_BUFFER_SIZE);
+
+               if (ProcReadBuffer == NULL) {
+                       LOGERR("failed to allocate buffer to provide proc data.\n");
+                       return -ENOMEM;
+               }
+       }
+
+       temp = ProcReadBuffer;
+
+       if ((*offset == 0) || (!ProcReadBufferValid)) {
+               DBGINF("calling info_proc_read_helper.\n");
+               /* if the read fails, then -1 will be returned */
+               totalBytes = info_proc_read_helper(&temp, &remaining_bytes);
+               ProcReadBufferValid = 1;
+       } else
+               totalBytes = strlen(ProcReadBuffer);
+
+       return simple_read_from_buffer(buf, len, offset,
+                                      ProcReadBuffer, totalBytes);
+}
+
+static ssize_t
+platformnumber_proc_read(struct file *file, char __user *buf,
+                        size_t len, loff_t *offset)
+{
+       int length = 0;
+       char *vbuf;
+       loff_t pos = *offset;
+
+       if (pos < 0)
+               return -EINVAL;
+
+       if (pos > 0 || !len)
+               return 0;
+
+       vbuf = kzalloc(len, GFP_KERNEL);
+       if (!vbuf)
+               return -ENOMEM;
+
+       length = sprintf(vbuf, "%d\n", PlatformNumber);
+
+       if (copy_to_user(buf, vbuf, length)) {
+               kfree(vbuf);
+               return -EFAULT;
+       }
+
+       kfree(vbuf);
+       *offset += length;
+       return length;
+}
+
+#ifdef UISLIB_TEST_PROC
+
+/* proc/uislib/vbus/<x>/info */
+static int
+proc_info_vbus_show(struct seq_file *m, void *v)
+{
+       struct bus_info *bus = m->private;
+       int i, devInfoCount, x;
+       char buf[999];
+
+       if (bus == NULL)
+               return 0;
+       seq_printf(m, "Client device / client driver info for %s partition (vbus #%d):\n",
+                  bus->partitionName, bus->busNo);
+       if ((bus->busChannelBytes == 0) || (bus->pBusChannel == NULL))
+               return 0;
+       devInfoCount =
+           (bus->busChannelBytes -
+            sizeof(ULTRA_VBUS_CHANNEL_PROTOCOL)) /
+           sizeof(ULTRA_VBUS_DEVICEINFO);
+       x = VBUSCHANNEL_devInfoToStringBuffer(bus->pBusChannel->ChpInfo, buf,
+                                             sizeof(buf) - 1, -1);
+       buf[x] = '\0';
+       seq_printf(m, "%s", buf);
+       x = VBUSCHANNEL_devInfoToStringBuffer(bus->pBusChannel->BusInfo,
+                                             buf, sizeof(buf) - 1, -1);
+       buf[x] = '\0';
+       seq_printf(m, "%s", buf);
+       for (i = 0; i < devInfoCount; i++) {
+               x = VBUSCHANNEL_devInfoToStringBuffer(bus->pBusChannel->
+                                                     DevInfo[i], buf,
+                                                     sizeof(buf) - 1, i);
+               if (x > 0) {
+                       buf[x] = '\0';
+                       seq_printf(m, "%s", buf);
+               }
+       }
+       return 0;
+}
+
+static ssize_t
+bus_proc_write(struct file *file, const char __user *buffer,
+              size_t count, loff_t *ppos)
+{
+       int server_flag = 0;
+       int i, action = 0xffff, result;
+       char buf[count];
+       CONTROLVM_MESSAGE msg;
+       U32 busNo, deviceCount;
+
+       memset(&msg, 0, sizeof(CONTROLVM_MESSAGE));
+
+       if (copy_from_user(buf, buffer, count)) {
+               LOGERR("echo > /proc/uislib/bus: copy_from_user ****FAILED.");
+               return -EFAULT;
+       }
+
+       i = sscanf(buf, "%x-%d-%d", &action, &busNo, &deviceCount);
+
+       /* if (i < 1), i.e., if we didn't even read the action field,
+       * then action will default to 0xffff and the code below will
+       * fall through the switch and print usage.
+       */
+       switch (action) {
+       case 0:
+               /* destroy a bus */
+               if (i != 2)
+                       break;
+               init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, server_flag);
+               msg.cmd.destroyBus.busNo = busNo;
+
+               result = destroy_bus(&msg, NULL);
+
+               if (result != CONTROLVM_RESP_SUCCESS) {
+                       LOGERR("echo 0-%d > /proc/uislib/bus {CONTROLVM_BUS_DESTROY Failed} Result(%d)",
+                            busNo, result);
+                       return -EFAULT;
+               }
+               return count;
+       case 1:
+               /* create a bus */
+               if (i != 3)
+                       break;
+               init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, server_flag);
+               msg.cmd.createBus.busNo = busNo;
+               msg.cmd.createBus.deviceCount = deviceCount;
+
+               result = create_bus(&msg, NULL);
+
+               if (result != CONTROLVM_RESP_SUCCESS) {
+                       LOGERR("echo 1-%d-%d > /proc/uislib/bus {CONTROLVM_BUS_CREATE Failed} Result(%d)",
+                            busNo, deviceCount, result);
+                       return -EFAULT;
+               }
+
+               return count;
+       default:
+               break;
+       }
+
+       LOGERR("USAGE: echo <action>-<busNo>... > /proc/uislib/bus");
+       LOGERR(" ");
+       LOGERR("Destruct Syntax     ControlVM Message Id");
+       LOGERR("---------------     ---------------------");
+       LOGERR("0-<busNo>       ==> CONTROLVM_BUS_DESTROY");
+       LOGERR(" ");
+       LOGERR("Construct Syntax            ControlVM Message Id");
+       LOGERR("-----------------------     -------------------- ");
+       LOGERR("1-<busNo>-<deviceCount> ==> CONTROLVM_BUS_CREATE");
+
+       return -EFAULT;
+}
+
+static ssize_t
+uislib_proc_read_writeonly(struct file *file, char __user *buffer,
+              size_t count, loff_t *ppos)
+{
+       return 0;
+}
+
+static ssize_t
+dev_proc_write(struct file *file, const char __user *buffer,
+              size_t count, loff_t *ppos)
+{
+       int server_flag = 0;
+       CONTROLVM_MESSAGE msg;
+       U32 busNo, devNo;
+       char buf[count];
+       unsigned int chanptr;
+       int type, i, action = 0xffff, result;
+
+       if (copy_from_user(buf, buffer, count)) {
+               LOGERR("echo > /proc/uislib/device: copy_from_user ****FAILED.");
+               return -EFAULT;
+       }
+
+       i = sscanf(buf, "%x-%d-%d-%x-%d",
+                  &action, &busNo, &devNo, &chanptr, &type);
+
+       switch (action) {
+       case 0:
+               if (i != 3)
+                       break;
+
+               /* destroy a device */
+               init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, server_flag);
+               msg.cmd.destroyDevice.busNo = busNo;
+               msg.cmd.destroyDevice.devNo = devNo;
+
+               result = destroy_device(&msg, NULL);
+
+               if (result != CONTROLVM_RESP_SUCCESS) {
+                       LOGERR("echo 0-%d-%d > /proc/uislib/device {CONTROLVM_DEVICE_DESTROY Failed} Result(%d)",
+                            busNo, devNo, result);
+                       return -EFAULT;
+               }
+
+               return count;
+
+       case 1:
+               if (i != 5)
+                       break;
+
+               /* create a device */
+               init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, server_flag);
+               msg.cmd.createDevice.busNo = busNo;
+               msg.cmd.createDevice.devNo = devNo;
+               msg.cmd.createDevice.channelAddr = __pa(chanptr);
+               msg.cmd.createDevice.channelBytes = MIN_IO_CHANNEL_SIZE;
+
+               if (type == 0)
+                       msg.cmd.createDevice.dataTypeGuid =
+                           UltraVhbaChannelProtocolGuid;
+               else if (type == 1)
+                       msg.cmd.createDevice.dataTypeGuid =
+                           UltraVnicChannelProtocolGuid;
+               else {
+                       LOGERR("echo 1-%d-%d-%x-<type> > /proc/uislib/devce failed: invalid device type %d.",
+                            busNo, devNo, chanptr, type);
+                       return -EFAULT;
+               }
+
+               result = create_device(&msg, NULL);
+
+               if (result != CONTROLVM_RESP_SUCCESS) {
+                       if (type == 0)
+                               LOGERR("echo 1-%d-%d-%x-0 > /proc/uislib/device {CONTROLVM_DEVICE_CREATE[vHBA] Failed} Result(%d)",
+                                    busNo, devNo, chanptr, result);
+                       else
+                               LOGERR("echo 1-%d-%d-%x-1 > /proc/uislib/device {CONTROLVM_DEVICE_CREATE[vNIC] Failed} Result(%d)",
+                                    busNo, devNo, chanptr, result);
+                       return -EFAULT;
+               }
+
+       default:
+               break;
+       }
+
+       LOGERR("USAGE: echo <action>-<busNo>-<devNo>... > /proc/uislib/device");
+       LOGERR(" ");
+       LOGERR("Destruct Syntax       ControlVM Message Id");
+       LOGERR("-----------------     ------------------------");
+       LOGERR("0-<busNo>-<devNo> ==> CONTROLVM_DEVICE_DESTROY");
+       LOGERR(" ");
+       LOGERR("Construct Syntax                       ControlVM Message Id");
+       LOGERR
+           ("----------------------------------     ----------------------- ");
+       LOGERR
+           ("1-<busNo>-<devNo>-<chanptr>-<type> ==> CONTROLVM_DEVICE_CREATE");
+       LOGERR("      <type = 0>: vHBA");
+       LOGERR("      <type = 1>: vNIC");
+       LOGERR(" ");
+
+       return -EFAULT;
+}
+
+static ssize_t
+cycles_before_wait_proc_write(struct file *file, const char __user *buffer,
+                             size_t count, loff_t *ppos)
+{
+       char buf[count];
+
+#define CYCLES_BEFORE_WAIT_USE_ERROR  { \
+       LOGERR("Incorrect Call Home Input.\n"); \
+       pr_info("Please pass Call Home Event Parameters in the form:\n"); \
+       pr_info("EventID Category Type[parameter1][parameter2][parameter3][parameter4][parameter5][parameter6]\n"); \
+       return -EFAULT; \
+}
+
+       if (count == 0)
+               CYCLES_BEFORE_WAIT_USE_ERROR;
+
+       if (copy_from_user(buf, buffer, count)) {
+               LOGERR("copy_from_user failed.\n");
+               return -EFAULT;
+       }
+       buf[count - 1] = '\0';  /* Replace the LF at the end of the
+                                * input with a NULL */
+       /* Pull out the cycles_before_wait must be decimal integer */
+       if (sscanf(buf, "%lld", &cycles_before_wait) != 1)
+               CYCLES_BEFORE_WAIT_USE_ERROR;
+
+       return count;
+}
+
+static ssize_t
+reset_counts_proc_write(struct file *file, const char __user *buffer,
+                       size_t count, loff_t *ppos)
+{
+       char buf[count];
+       unsigned long long new_value;
+       struct bus_info *bus;
+       int i;
+
+#define RESET_COUNTS_USE_ERROR  { \
+       LOGERR("Incorrect reset_counts Input.\n"); \
+       pr_info("Please pass the new value for the counters:\n"); \
+       pr_info("e.g. echo 0 > reset_counts\n"); \
+       return -EFAULT; \
+       }
+
+       if (count == 0)
+               RESET_COUNTS_USE_ERROR;
+
+       if (copy_from_user(buf, buffer, count)) {
+               LOGERR("copy_from_user failed.\n");
+               return -EFAULT;
+       }
+       buf[count - 1] = '\0';  /* Replace the LF at the end of the
+                                * input with a NULL */
+       /* Pull out the reset_counts must be decimal integer */
+       if (sscanf(buf, "%llu", &new_value) != 1)
+               RESET_COUNTS_USE_ERROR;
+       read_lock(&BusListLock);
+       for (bus = BusListHead; bus; bus = bus->next) {
+
+               for (i = 0; i < bus->deviceCount; i++) {
+                       if (bus->device[i]) {
+                               bus->device[i]->first_busy_cnt = new_value;
+                               bus->device[i]->moved_to_tail_cnt = new_value;
+                               bus->device[i]->last_on_list_cnt = new_value;
+                       }
+               }
+       }
+       read_unlock(&BusListLock);
+       tot_moved_to_tail_cnt = new_value;
+       tot_wait_cnt = new_value;
+       tot_wakeup_cnt = new_value;
+       tot_schedule_cnt = new_value;
+       return count;
+}
+
+static ssize_t
+smart_wakeup_proc_write(struct file *file, const char __user *buffer,
+                       size_t count, loff_t *ppos)
+{
+       char buf[count];
+       int new_value;
+
+#define SMART_WAKEUP_USE_ERROR  { \
+       LOGERR("Incorrect smart_wakeup Input 0 disables smart_wakeup, and 1 enables smart_wakeup.\n"); \
+       pr_info("echo 0 > smart_wakeup\n"); \
+       pr_info("echo 1 > smart_wakeup\n"); \
+       return -EFAULT; \
+       }
+
+       if (count == 0)
+               SMART_WAKEUP_USE_ERROR;
+
+       if (copy_from_user(buf, buffer, count)) {
+               LOGERR("copy_from_user failed.\n");
+               return -EFAULT;
+       }
+       buf[count - 1] = '\0';  /* Replace the LF at the end of the
+                                * input with a NULL */
+       /* Pull out the smart_wakeup must be decimal integer */
+       if (sscanf(buf, "%d", &new_value) != 1)
+               SMART_WAKEUP_USE_ERROR;
+       en_smart_wakeup = new_value;
+       return count;
+}
+
+static ssize_t
+test_proc_write(struct file *file, const char __user *buffer,
+               size_t count, loff_t *ppos)
+{
+       int i, action = 0xffff;
+       char buf[count];
+       CONTROLVM_MESSAGE msg;
+       S64 vrtc_offset;
+
+       memset(&msg, 0, sizeof(CONTROLVM_MESSAGE));
+
+       if (copy_from_user(buf, buffer, count)) {
+               LOGERR("copy_from_user ****FAILED.\n");
+               return -EFAULT;
+       }
+
+       i = sscanf(buf, "%x", &action);
+
+       /* if (i < 1), i.e., if we didn't even read the action field,
+       * then action will default to 0xffff and the code below will
+       * fall through the switch and print usage. */
+       switch (action) {
+       case 6:
+               msg.hdr.Id = CONTROLVM_CHIPSET_STOP;
+               msg.hdr.Flags.responseExpected = 1;
+               stop_chipset(&msg, NULL);
+               break;
+       case 7:
+               vrtc_offset = 0;
+               LOGERR("about to issue QUERY vrtc_offset=%LX", vrtc_offset);
+               vrtc_offset = Issue_VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET();
+               LOGERR("result is vrtc_offset=%LX", vrtc_offset);
+               break;
+       case 8:
+               vrtc_offset = 60;
+               LOGERR("about to increase physical time by 0x%LX seconds",
+                      vrtc_offset);
+               vrtc_offset = Issue_VMCALL_UPDATE_PHYSICAL_TIME(vrtc_offset);
+               break;
+       case 9:
+               vrtc_offset = -60;
+               LOGERR("about to decrease physical time by 0x%LX seconds",
+                      vrtc_offset);
+               vrtc_offset = Issue_VMCALL_UPDATE_PHYSICAL_TIME(vrtc_offset);
+               break;
+       default:
+               LOGERR("usage: 6 for CHIPSET_STOP\n");
+               LOGERR("       7 for VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET()\n");
+               LOGERR("       8 for VMCALL_UPDATE_PHYSICAL_TIME(60)\n");
+               LOGERR("       9 for VMCALL_UPDATE_PHYSICAL_TIME(-60)\n");
+               return -EFAULT;
+               break;
+       }
+       return count;
+}
+
+#endif                         /* UISLIB_TEST_PROC */
+static struct device_info *
+find_dev(U32 busNo, U32 devNo)
+{
+       struct bus_info *bus;
+       struct device_info *dev = NULL;
+
+       read_lock(&BusListLock);
+       for (bus = BusListHead; bus; bus = bus->next) {
+               if (bus->busNo == busNo) {
+                       /* make sure the device number is valid */
+                       if (devNo >= bus->deviceCount) {
+                               LOGERR("%s bad busNo, devNo=%d,%d",
+                                      __func__,
+                                      (int) (busNo), (int) (devNo));
+                               goto Away;
+                       }
+                       dev = bus->device[devNo];
+                       if (!dev)
+                               LOGERR("%s bad busNo, devNo=%d,%d",
+                                      __func__,
+                                      (int) (busNo), (int) (devNo));
+                       goto Away;
+               }
+       }
+Away:
+       read_unlock(&BusListLock);
+       return dev;
+}
+
+/*  This thread calls the "interrupt" function for each device that has
+ *  enabled such using uislib_enable_channel_interrupts().  The "interrupt"
+ *  function typically reads and processes the devices's channel input
+ *  queue.  This thread repeatedly does this, until the thread is told to stop
+ *  (via uisthread_stop()).  Sleeping rules:
+ *  - If we have called the "interrupt" function for all devices, and all of
+ *    them have reported "nothing processed" (returned 0), then we will go to
+ *    sleep for a maximum of POLLJIFFIES_NORMAL jiffies.
+ *  - If anyone calls uislib_force_channel_interrupt(), the above jiffy
+ *    sleep will be interrupted, and we will resume calling the "interrupt"
+ *    function for all devices.
+ *  - The list of devices is dynamically re-ordered in order to
+ *    attempt to preserve fairness.  Whenever we spin thru the list of
+ *    devices and call the dev->interrupt() function, if we find
+ *    devices which report that there is still more work to do, the
+ *    the first such device we find is moved to the end of the device
+ *    list.  This ensures that extremely busy devices don't starve out
+ *    less-busy ones.
+ *
+ */
+static int
+Process_Incoming(void *v)
+{
+       unsigned long long cur_cycles, old_cycles, idle_cycles, delta_cycles;
+       struct list_head *new_tail = NULL;
+       int i;
+       UIS_DAEMONIZE("dev_incoming");
+       for (i = 0; i < 16; i++) {
+               old_cycles = get_cycles();
+               wait_event_timeout(Wakeup_Polling_Device_Channels,
+                                  0, POLLJIFFIES_NORMAL);
+               cur_cycles = get_cycles();
+               if (wait_cycles == 0) {
+                       wait_cycles = (cur_cycles - old_cycles);
+               } else {
+                       if (wait_cycles < (cur_cycles - old_cycles))
+                               wait_cycles = (cur_cycles - old_cycles);
+               }
+       }
+       LOGINF("wait_cycles=%llu", wait_cycles);
+       cycles_before_wait = wait_cycles;
+       idle_cycles = 0;
+       Go_Polling_Device_Channels = 0;
+       while (1) {
+               struct list_head *lelt, *tmp;
+               struct device_info *dev = NULL;
+
+               /* poll each channel for input */
+               LOCKSEM_UNINTERRUPTIBLE(&Lock_Polling_Device_Channels);
+               new_tail = NULL;
+               list_for_each_safe(lelt, tmp, &List_Polling_Device_Channels) {
+                       int rc = 0;
+                       dev = list_entry(lelt, struct device_info,
+                                        list_polling_device_channels);
+                       LOCKSEM_UNINTERRUPTIBLE(&dev->interrupt_callback_lock);
+                       if (dev->interrupt)
+                               rc = dev->interrupt(dev->interrupt_context);
+                       else
+                               continue;
+                       UNLOCKSEM(&dev->interrupt_callback_lock);
+                       if (rc) {
+                               /* dev->interrupt returned, but there
+                               * is still more work to do.
+                               * Reschedule work to occur as soon as
+                               * possible. */
+                               idle_cycles = 0;
+                               if (new_tail == NULL) {
+                                       dev->first_busy_cnt++;
+                                       if (!
+                                           (list_is_last
+                                            (lelt,
+                                             &List_Polling_Device_Channels))) {
+                                               new_tail = lelt;
+                                               dev->moved_to_tail_cnt++;
+                                       } else
+                                               dev->last_on_list_cnt++;
+                               }
+
+                       }
+                       if (Incoming_ThreadInfo.should_stop)
+                               break;
+               }
+               if (new_tail != NULL) {
+                       tot_moved_to_tail_cnt++;
+                       list_move_tail(new_tail, &List_Polling_Device_Channels);
+               }
+               UNLOCKSEM(&Lock_Polling_Device_Channels);
+               cur_cycles = get_cycles();
+               delta_cycles = cur_cycles - old_cycles;
+               old_cycles = cur_cycles;
+
+               /* At this point, we have scanned thru all of the
+               * channels, and at least one of the following is true:
+               * - there is no input waiting on any of the channels
+               * - we have received a signal to stop this thread
+               */
+               if (Incoming_ThreadInfo.should_stop)
+                       break;
+               if (en_smart_wakeup == 0xFF) {
+                       LOGINF("en_smart_wakeup set to 0xff, to force exiting process_incoming");
+                       break;
+               }
+               /* wait for POLLJIFFIES_NORMAL jiffies, or until
+               * someone wakes up Wakeup_Polling_Device_Channels,
+               * whichever comes first only do a wait when we have
+               * been idle for cycles_before_wait cycles.
+               */
+               if (idle_cycles > cycles_before_wait) {
+                       Go_Polling_Device_Channels = 0;
+                       tot_wait_cnt++;
+                       wait_event_timeout(Wakeup_Polling_Device_Channels,
+                                          Go_Polling_Device_Channels,
+                                          POLLJIFFIES_NORMAL);
+                       Go_Polling_Device_Channels = 1;
+               } else {
+                       tot_schedule_cnt++;
+                       schedule();
+                       idle_cycles = idle_cycles + delta_cycles;
+               }
+       }
+       DBGINF("exiting.\n");
+       complete_and_exit(&Incoming_ThreadInfo.has_stopped, 0);
+}
+
+static BOOL
+Initialize_incoming_thread(void)
+{
+       if (Incoming_Thread_Started)
+               return TRUE;
+       if (!uisthread_start(&Incoming_ThreadInfo,
+                            &Process_Incoming, NULL, "dev_incoming")) {
+               LOGERR("uisthread_start Initialize_incoming_thread ****FAILED");
+               return FALSE;
+       }
+       Incoming_Thread_Started = TRUE;
+       return TRUE;
+}
+
+/*  Add a new device/channel to the list being processed by
+ *  Process_Incoming().
+ *  <interrupt> - indicates the function to call periodically.
+ *  <interrupt_context> - indicates the data to pass to the <interrupt>
+ *                        function.
+ */
+void
+uislib_enable_channel_interrupts(U32 busNo, U32 devNo,
+                                int (*interrupt)(void *),
+                                void *interrupt_context)
+{
+       struct device_info *dev;
+       dev = find_dev(busNo, devNo);
+       if (!dev) {
+               LOGERR("%s busNo=%d, devNo=%d", __func__, (int) (busNo),
+                      (int) (devNo));
+               return;
+       }
+       LOCKSEM_UNINTERRUPTIBLE(&Lock_Polling_Device_Channels);
+       Initialize_incoming_thread();
+       dev->interrupt = interrupt;
+       dev->interrupt_context = interrupt_context;
+       dev->polling = TRUE;
+       list_add_tail(&(dev->list_polling_device_channels),
+                     &List_Polling_Device_Channels);
+       UNLOCKSEM(&Lock_Polling_Device_Channels);
+}
+EXPORT_SYMBOL_GPL(uislib_enable_channel_interrupts);
+
+/*  Remove a device/channel from the list being processed by
+ *  Process_Incoming().
+ */
+void
+uislib_disable_channel_interrupts(U32 busNo, U32 devNo)
+{
+       struct device_info *dev;
+       dev = find_dev(busNo, devNo);
+       if (!dev) {
+               LOGERR("%s busNo=%d, devNo=%d", __func__, (int) (busNo),
+                      (int) (devNo));
+               return;
+       }
+       LOCKSEM_UNINTERRUPTIBLE(&Lock_Polling_Device_Channels);
+       list_del(&dev->list_polling_device_channels);
+       dev->polling = FALSE;
+       dev->interrupt = NULL;
+       UNLOCKSEM(&Lock_Polling_Device_Channels);
+}
+EXPORT_SYMBOL_GPL(uislib_disable_channel_interrupts);
+
+static void
+do_wakeup_polling_device_channels(struct work_struct *dummy)
+{
+       if (!Go_Polling_Device_Channels) {
+               Go_Polling_Device_Channels = 1;
+               wake_up(&Wakeup_Polling_Device_Channels);
+       }
+}
+
+DECLARE_WORK(Work_wakeup_polling_device_channels,
+            do_wakeup_polling_device_channels);
+
+/*  Call this function when you want to send a hint to Process_Incoming() that
+ *  your device might have more requests.
+ */
+void
+uislib_force_channel_interrupt(U32 busNo, U32 devNo)
+{
+       if (en_smart_wakeup == 0)
+               return;
+       if (Go_Polling_Device_Channels)
+               return;
+       /* The point of using schedule_work() instead of just doing
+        * the work inline is to force a slight delay before waking up
+        * the Process_Incoming() thread.
+        */
+       tot_wakeup_cnt++;
+       schedule_work(&Work_wakeup_polling_device_channels);
+}
+EXPORT_SYMBOL_GPL(uislib_force_channel_interrupt);
+
+/*****************************************************/
+/* Module Init & Exit functions                      */
+/*****************************************************/
+
+static int __init
+uislib_mod_init(void)
+{
+
+       LOGINF("MONITORAPIS");
+
+       LOGINF("sizeof(struct uiscmdrsp):%lu bytes\n",
+              (ulong) sizeof(struct uiscmdrsp));
+       LOGINF("sizeof(struct phys_info):%lu\n",
+              (ulong) sizeof(struct phys_info));
+       LOGINF("sizeof(uiscmdrsp_scsi):%lu\n",
+              (ulong) sizeof(struct uiscmdrsp_scsi));
+       LOGINF("sizeof(uiscmdrsp_net):%lu\n",
+              (ulong) sizeof(struct uiscmdrsp_net));
+       LOGINF("sizeof(CONTROLVM_MESSAGE):%lu bytes\n",
+              (ulong) sizeof(CONTROLVM_MESSAGE));
+       LOGINF("sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL):%lu bytes\n",
+              (ulong) sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL));
+       LOGINF("sizeof(CHANNEL_HEADER):%lu bytes\n",
+              (ulong) sizeof(CHANNEL_HEADER));
+       LOGINF("sizeof(ULTRA_IO_CHANNEL_PROTOCOL):%lu bytes\n",
+              (ulong) sizeof(ULTRA_IO_CHANNEL_PROTOCOL));
+       LOGINF("SIZEOF_CMDRSP:%lu bytes\n", SIZEOF_CMDRSP);
+       LOGINF("SIZEOF_PROTOCOL:%lu bytes\n", SIZEOF_PROTOCOL);
+
+       /* initialize global pointers to NULL */
+       BusListHead = NULL;
+       BusListCount = MaxBusCount = 0;
+       rwlock_init(&BusListLock);
+       VirtControlChanFunc = NULL;
+
+       /* Issue VMCALL_GET_CONTROLVM_ADDR to get CtrlChanPhysAddr and
+        * then map this physical address to a virtual address. */
+       POSTCODE_LINUX_2(DRIVER_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+
+       /* create the proc entries for the channels */
+       uislib_proc_dir = proc_mkdir(DIR_PROC_ENTRY, NULL);
+       /* (e.g., for /proc/uislib/vbus/<x>/info) */
+       uislib_proc_vbus_dir = proc_mkdir(DIR_VBUS_PROC_ENTRY, uislib_proc_dir);
+
+       vnic_proc_entry = proc_create(VNIC_PROC_ENTRY_FN, 0, uislib_proc_dir,
+                                     &proc_vnic_fops);
+       SET_PROC_OWNER(vnic_proc_entry, THIS_MODULE);
+
+       /* for testing purposes only, create the proc entries for
+        * enqueuing Control Channel messages */
+       chipset_proc_entry =
+           proc_create(CHIPSET_PROC_ENTRY_FN, 0, uislib_proc_dir,
+                       &proc_chipset_fops);
+       SET_PROC_OWNER(chipset_proc_entry, THIS_MODULE);
+
+       info_proc_entry = proc_create(INFO_PROC_ENTRY_FN, 0, uislib_proc_dir,
+                                     &proc_info_fops);
+       SET_PROC_OWNER(info_proc_entry, THIS_MODULE);
+
+       platformnumber_proc_entry =
+           proc_create(PLATFORMNUMBER_PROC_ENTRY_FN, 0, uislib_proc_dir,
+                       &proc_platformnumber_fops);
+       SET_PROC_OWNER(platformnumberinfo_proc_entry, THIS_MODULE);
+
+       cycles_before_wait_proc_entry =
+           proc_create(CYCLES_BEFORE_WAIT_PROC_ENTRY_FN, 0, uislib_proc_dir,
+                       &proc_cycles_before_wait_fops);
+       SET_PROC_OWNER(cycles_before_wait_proc_entry, THIS_MODULE);
+
+       reset_counts_proc_entry =
+           proc_create(RESET_COUNTS_PROC_ENTRY_FN, 0, uislib_proc_dir,
+                       &proc_reset_counts_fops);
+       SET_PROC_OWNER(reset_counts_proc_entry, THIS_MODULE);
+
+       smart_wakeup_proc_entry =
+           proc_create(SMART_WAKEUP_PROC_ENTRY_FN, 0, uislib_proc_dir,
+                       &proc_smart_wakeup_fops);
+       SET_PROC_OWNER(smart_wakeup_proc_entry, THIS_MODULE);
+
+#ifdef UISLIB_TEST_PROC
+       test_proc_entry = proc_create(TEST_PROC_ENTRY_FN, 0, uislib_proc_dir,
+                                     &proc_test_fops);
+       SET_PROC_OWNER(test_proc_entry, THIS_MODULE);
+
+       bus_proc_entry = proc_create(BUS_PROC_ENTRY_FN, 0, uislib_proc_dir,
+                                    &proc_bus_fops);
+       SET_PROC_OWNER(bus_proc_entry, THIS_MODULE);
+
+       dev_proc_entry = proc_create(DEV_PROC_ENTRY_FN, 0, uislib_proc_dir,
+                                    &proc_dev_fops);
+       SET_PROC_OWNER(dev_proc_entry, THIS_MODULE);
+#endif                         /* UISLIB_TEST_PROC */
+       POSTCODE_LINUX_3(DRIVER_EXIT_PC, 0, POSTCODE_SEVERITY_INFO);
+       return 0;
+}
+
+static void __exit
+uislib_mod_exit(void)
+{
+       if (disable_proc_entry)
+               remove_proc_entry(DISABLE_PROC_ENTRY_FN, uislib_proc_dir);
+       if (cycles_before_wait_proc_entry)
+               remove_proc_entry(CYCLES_BEFORE_WAIT_PROC_ENTRY_FN,
+                                 uislib_proc_dir);
+       if (reset_counts_proc_entry)
+               remove_proc_entry(RESET_COUNTS_PROC_ENTRY_FN, uislib_proc_dir);
+       if (smart_wakeup_proc_entry)
+               remove_proc_entry(SMART_WAKEUP_PROC_ENTRY_FN, uislib_proc_dir);
+       if (ctrlchan_proc_entry)
+               remove_proc_entry(CTRLCHAN_PROC_ENTRY_FN, uislib_proc_dir);
+       if (pmem_proc_entry)
+               remove_proc_entry(PMEM_PROC_ENTRY_FN, uislib_proc_dir);
+       if (info_proc_entry)
+               remove_proc_entry(INFO_PROC_ENTRY_FN, uislib_proc_dir);
+       if (switch_proc_entry)
+               remove_proc_entry(SWITCH_PROC_ENTRY_FN, uislib_proc_dir);
+       if (extport_proc_entry)
+               remove_proc_entry(EXTPORT_PROC_ENTRY_FN, uislib_proc_dir);
+       if (platformnumber_proc_entry)
+               remove_proc_entry(PLATFORMNUMBER_PROC_ENTRY_FN,
+                                 uislib_proc_dir);
+       if (bus_proc_entry)
+               remove_proc_entry(BUS_PROC_ENTRY_FN, uislib_proc_dir);
+       if (dev_proc_entry)
+               remove_proc_entry(DEV_PROC_ENTRY_FN, uislib_proc_dir);
+       if (vnic_proc_entry)
+               remove_proc_entry(VNIC_PROC_ENTRY_FN, uislib_proc_dir);
+       if (chipset_proc_entry)
+               remove_proc_entry(CHIPSET_PROC_ENTRY_FN, uislib_proc_dir);
+       if (uislib_proc_vbus_dir)
+               remove_proc_entry(DIR_VBUS_PROC_ENTRY, uislib_proc_dir);
+       if (uislib_proc_dir)
+               remove_proc_entry(DIR_PROC_ENTRY, NULL);
+
+       if (ProcReadBuffer) {
+               UISVFREE(ProcReadBuffer, PROC_READ_BUFFER_SIZE);
+               ProcReadBuffer = NULL;
+       }
+
+       DBGINF("goodbye.\n");
+       return;
+}
+
+module_init(uislib_mod_init);
+module_exit(uislib_mod_exit);
+
+int uis_mandatory_services = -1;
+
+module_param_named(mandatory_services, uis_mandatory_services,
+                  int, S_IRUGO);
+MODULE_PARM_DESC(uis_mandatory_services,
+                "number of server drivers we expect to register (default=-1 for legacy behavior)");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Usha Srinivasan");
+MODULE_ALIAS("uislib");
+  /* this is extracted during depmod and kept in modules.dep */
diff --git a/drivers/staging/unisys/uislib/uisqueue.c b/drivers/staging/unisys/uislib/uisqueue.c
new file mode 100644 (file)
index 0000000..67e413f
--- /dev/null
@@ -0,0 +1,166 @@
+/* uisqueue.c
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ */
+
+/* @ALL_INSPECTED */
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "uisutils.h"
+
+#include "chanstub.h"
+
+/* this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages */
+#define CURRENT_FILE_PC UISLIB_PC_uisqueue_c
+#define __MYFILE__ "uisqueue.c"
+
+#define RETVOID    do { goto Away; } while (0)
+#define RETINT(x)  do { rc = (x); goto Away; } while (0)
+#define RETPTR(x)  do { rc = (x); goto Away; } while (0)
+#define RETBOOL(x) do { rc = (x); goto Away; } while (0)
+
+#define CHECK_CACHE_ALIGN 0
+
+/*****************************************************/
+/* Exported functions                                */
+/*****************************************************/
+unsigned long long
+uisqueue_InterlockedOr(volatile unsigned long long *Target,
+                      unsigned long long Set)
+{
+       unsigned long long i;
+       unsigned long long j;
+
+       j = *Target;
+       do {
+               i = j;
+               j = uislibcmpxchg64((unsigned long long *) Target,
+                                   i, i | Set, sizeof(*(Target)));
+
+       } while (i != j);
+
+       return j;
+}
+EXPORT_SYMBOL_GPL(uisqueue_InterlockedOr);
+
+unsigned long long
+uisqueue_InterlockedAnd(volatile unsigned long long *Target,
+                       unsigned long long Set)
+{
+       unsigned long long i;
+       unsigned long long j;
+
+       j = *Target;
+       do {
+               i = j;
+               j = uislibcmpxchg64((unsigned long long *) Target,
+                                   i, i & Set, sizeof(*(Target)));
+
+       } while (i != j);
+
+       return j;
+}
+EXPORT_SYMBOL_GPL(uisqueue_InterlockedAnd);
+
+static U8
+do_locked_client_insert(struct uisqueue_info *queueinfo,
+                       unsigned int whichqueue,
+                       void *pSignal,
+                       spinlock_t *lock,
+                       unsigned char issueInterruptIfEmpty,
+                       U64 interruptHandle, U8 *channelId)
+{
+       unsigned long flags;
+       unsigned char queueWasEmpty;
+       unsigned int locked = 0;
+       unsigned int acquired = 0;
+       U8 rc = 0;
+
+       spin_lock_irqsave(lock, flags);
+       locked = 1;
+
+       if (!ULTRA_CHANNEL_CLIENT_ACQUIRE_OS(queueinfo->chan, channelId, NULL))
+               RETINT(0);
+
+       acquired = 1;
+
+       queueWasEmpty = SignalQueueIsEmpty(queueinfo->chan, whichqueue);
+       if (!SignalInsert(queueinfo->chan, whichqueue, pSignal))
+               RETINT(0);
+       ULTRA_CHANNEL_CLIENT_RELEASE_OS(queueinfo->chan, channelId, NULL);
+       acquired = 0;
+       spin_unlock_irqrestore(lock, flags);
+       locked = 0;
+
+       queueinfo->packets_sent++;
+
+       RETINT(1);
+
+Away:
+       if (acquired) {
+               ULTRA_CHANNEL_CLIENT_RELEASE_OS(queueinfo->chan, channelId,
+                                               NULL);
+               acquired = 0;
+       }
+       if (locked) {
+               spin_unlock_irqrestore((spinlock_t *) lock, flags);
+               locked = 0;
+       }
+
+       return rc;
+}
+
+int
+uisqueue_put_cmdrsp_with_lock_client(struct uisqueue_info *queueinfo,
+                                    struct uiscmdrsp *cmdrsp,
+                                    unsigned int whichqueue,
+                                    void *insertlock,
+                                    unsigned char issueInterruptIfEmpty,
+                                    U64 interruptHandle,
+                                    char oktowait, U8 *channelId)
+{
+       while (!do_locked_client_insert(queueinfo, whichqueue, cmdrsp,
+                                       (spinlock_t *) insertlock,
+                                       issueInterruptIfEmpty,
+                                       interruptHandle, channelId)) {
+               if (oktowait != OK_TO_WAIT) {
+                       LOGERR("****FAILED SignalInsert failed; cannot wait; insert aborted\n");
+                       return 0;       /* failed to queue */
+               }
+               /* try again */
+               LOGERR("****FAILED SignalInsert failed; waiting to try again\n");
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(msecs_to_jiffies(10));
+       }
+       return 1;
+}
+EXPORT_SYMBOL_GPL(uisqueue_put_cmdrsp_with_lock_client);
+
+/* uisqueue_get_cmdrsp gets the cmdrsp entry at the head of the queue
+ * returns NULL if queue is empty */
+int
+uisqueue_get_cmdrsp(struct uisqueue_info *queueinfo,
+                   void *cmdrsp, unsigned int whichqueue)
+{
+       if (!SignalRemove(queueinfo->chan, whichqueue, cmdrsp))
+               return 0;
+
+       queueinfo->packets_received++;
+
+       return 1;               /* Success */
+}
+EXPORT_SYMBOL_GPL(uisqueue_get_cmdrsp);
diff --git a/drivers/staging/unisys/uislib/uisthread.c b/drivers/staging/unisys/uislib/uisthread.c
new file mode 100644 (file)
index 0000000..ecf4bfc
--- /dev/null
@@ -0,0 +1,85 @@
+/* uisthread.c
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ */
+
+/* @ALL_INSPECTED */
+#include <asm/processor.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include "uniklog.h"
+#include "uisutils.h"
+#include "uisthread.h"
+
+#define KILL(a, b, c) kill_pid(find_vpid(a), b, c)
+
+/* this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages
+ */
+#define CURRENT_FILE_PC UISLIB_PC_uisthread_c
+#define __MYFILE__ "uisthread.c"
+
+/*****************************************************/
+/* Exported functions                                */
+/*****************************************************/
+
+/* returns 0 for failure, 1 for success */
+int
+uisthread_start(struct uisthread_info *thrinfo,
+               int (*threadfn)(void *), void *thrcontext, char *name)
+{
+       thrinfo->should_stop = 0;
+       /* used to stop the thread */
+       init_completion(&thrinfo->has_stopped);
+       thrinfo->task = kthread_create(threadfn, thrcontext, name, NULL);
+       if (thrinfo->task == NULL) {
+               thrinfo->id = 0;
+               return 0;       /* failure */
+       }
+       thrinfo->id = thrinfo->task->pid;
+       wake_up_process(thrinfo->task);
+       LOGINF("started thread pid:%d\n", thrinfo->id);
+       return 1;
+
+}
+EXPORT_SYMBOL_GPL(uisthread_start);
+
+void
+uisthread_stop(struct uisthread_info *thrinfo)
+{
+       int ret;
+       int stopped = 0;
+       if (thrinfo->id == 0)
+               return;         /* thread not running */
+
+       LOGINF("uisthread_stop stopping id:%d\n", thrinfo->id);
+       thrinfo->should_stop = 1;
+       ret = KILL(thrinfo->id, SIGHUP, 1);
+       if (ret) {
+               LOGERR("unable to signal thread %d\n", ret);
+       } else {
+               /* give up if the thread has NOT died in 1 minute */
+               if (wait_for_completion_timeout(&thrinfo->has_stopped, 60 * HZ))
+                       stopped = 1;
+               else
+                       LOGERR("timed out trying to signal thread\n");
+       }
+       if (stopped) {
+               LOGINF("uisthread_stop stopped id:%d\n", thrinfo->id);
+               thrinfo->id = 0;
+       }
+}
+EXPORT_SYMBOL_GPL(uisthread_stop);
diff --git a/drivers/staging/unisys/uislib/uisutils.c b/drivers/staging/unisys/uislib/uisutils.c
new file mode 100644 (file)
index 0000000..974f8bc
--- /dev/null
@@ -0,0 +1,349 @@
+/* uisutils.c
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <commontypes.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include "uniklog.h"
+#include "uisutils.h"
+#include "version.h"
+#include "vbushelper.h"
+#include "guidutils.h"
+#include <linux/skbuff.h>
+#ifdef CONFIG_HIGHMEM
+#include <linux/highmem.h>
+#endif
+
+/* this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages
+ */
+#define CURRENT_FILE_PC UISLIB_PC_uisutils_c
+#define __MYFILE__ "uisutils.c"
+
+/* exports */
+atomic_t UisUtils_Registered_Services = ATOMIC_INIT(0);
+                                       /* num registrations via
+                                        * uisctrl_register_req_handler() or
+                                        * uisctrl_register_req_handler_ex() */
+
+
+/*****************************************************/
+/* Utility functions                                 */
+/*****************************************************/
+
+int
+util_add_proc_line_ex(int *total, char **buffer, int *buffer_remaining,
+                     char *format, ...)
+{
+       va_list args;
+       int len;
+
+       DBGINF("buffer = 0x%p : *buffer = 0x%p.\n", buffer, *buffer);
+       va_start(args, format);
+       len = vsnprintf(*buffer, *buffer_remaining, format, args);
+       if (len >= *buffer_remaining) {
+               *buffer += *buffer_remaining;
+               *total += *buffer_remaining;
+               *buffer_remaining = 0;
+               LOGERR("bytes remaining is too small!\n");
+               return -1;
+       }
+       *buffer_remaining -= len;
+       *buffer += len;
+       *total += len;
+       return len;
+}
+EXPORT_SYMBOL_GPL(util_add_proc_line_ex);
+
+int
+uisctrl_register_req_handler(int type, void *fptr,
+                            ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo)
+{
+       LOGINF("type = %d, fptr = 0x%p.\n", type, fptr);
+
+       switch (type) {
+       case 2:
+               if (fptr) {
+                       if (!VirtControlChanFunc)
+                               atomic_inc(&UisUtils_Registered_Services);
+                       VirtControlChanFunc = fptr;
+               } else {
+                       if (VirtControlChanFunc)
+                               atomic_dec(&UisUtils_Registered_Services);
+                       VirtControlChanFunc = NULL;
+               }
+               break;
+
+       default:
+               LOGERR("invalid type %d.\n", type);
+               return 0;
+       }
+       if (chipset_DriverInfo)
+               BusDeviceInfo_Init(chipset_DriverInfo,
+                                  "chipset", "uislib",
+                                  VERSION, NULL, __DATE__, __TIME__);
+
+       return 1;
+}
+EXPORT_SYMBOL_GPL(uisctrl_register_req_handler);
+
+int
+uisctrl_register_req_handler_ex(GUID switchTypeGuid,
+                               const char *switch_type_name,
+                               int (*controlfunc)(struct io_msgs *),
+                               unsigned long min_channel_bytes,
+                               int (*Server_Channel_Ok)(unsigned long
+                                                         channelBytes),
+                               int (*Server_Channel_Init)
+                                (void *x, unsigned char *clientStr,
+                                 U32 clientStrLen, U64 bytes),
+                               ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo)
+{
+       char s[99];
+       ReqHandlerInfo_t *pReqHandlerInfo;
+       int rc = 0;             /* assume failure */
+       LOGINF("type=%s, controlfunc=0x%p.\n",
+              GUID_format1(&switchTypeGuid, s), controlfunc);
+       if (!controlfunc) {
+               LOGERR("%s: controlfunc must be supplied\n",
+                      GUID_format1(&switchTypeGuid, s));
+               goto Away;
+       }
+       if (!Server_Channel_Ok) {
+               LOGERR("%s: Server_Channel_Ok must be supplied\n",
+                      GUID_format1(&switchTypeGuid, s));
+               goto Away;
+       }
+       if (!Server_Channel_Init) {
+               LOGERR("%s: Server_Channel_Init must be supplied\n",
+                      GUID_format1(&switchTypeGuid, s));
+               goto Away;
+       }
+       pReqHandlerInfo = ReqHandlerAdd(switchTypeGuid,
+                                       switch_type_name,
+                                       controlfunc,
+                                       min_channel_bytes,
+                                       Server_Channel_Ok, Server_Channel_Init);
+       if (!pReqHandlerInfo) {
+               LOGERR("failed to add %s to server list\n",
+                      GUID_format1(&switchTypeGuid, s));
+               goto Away;
+       }
+
+       atomic_inc(&UisUtils_Registered_Services);
+       rc = 1;                 /* success */
+Away:
+       if (rc) {
+               if (chipset_DriverInfo)
+                       BusDeviceInfo_Init(chipset_DriverInfo,
+                                          "chipset", "uislib",
+                                          VERSION, NULL,
+                                          __DATE__, __TIME__);
+       } else
+               LOGERR("failed to register type %s.\n",
+                      GUID_format1(&switchTypeGuid, s));
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(uisctrl_register_req_handler_ex);
+
+int
+uisctrl_unregister_req_handler_ex(GUID switchTypeGuid)
+{
+       char s[99];
+       int rc = 0;             /* assume failure */
+       LOGINF("type=%s.\n", GUID_format1(&switchTypeGuid, s));
+       if (ReqHandlerDel(switchTypeGuid) < 0) {
+               LOGERR("failed to remove %s from server list\n",
+                      GUID_format1(&switchTypeGuid, s));
+               goto Away;
+       }
+       atomic_dec(&UisUtils_Registered_Services);
+       rc = 1;                 /* success */
+Away:
+       if (!rc)
+               LOGERR("failed to unregister type %s.\n",
+                      GUID_format1(&switchTypeGuid, s));
+       return rc;
+}
+EXPORT_SYMBOL_GPL(uisctrl_unregister_req_handler_ex);
+
+/*
+ * unsigned int util_copy_fragsinfo_from_skb(unsigned char *calling_ctx,
+ *                                          void *skb_in,
+ *                                          unsigned int firstfraglen,
+ *                                          unsigned int frags_max,
+ *                                          struct phys_info frags[])
+ *
+ *     calling_ctx - input -   a string that is displayed to show
+ *                             who called * this func
+ *     void *skb_in -  skb whose frag info we're copying type is hidden so we
+ *                     don't need to include skbbuff in uisutils.h which is
+ *                     included in non-networking code.
+ *     unsigned int firstfraglen - input - length of first fragment in skb
+ *     unsigned int frags_max - input - max len of frags array
+ *     struct phys_info frags[] - output - frags array filled in on output
+ *                                         return value indicates number of
+ *                                         entries filled in frags
+ */
+unsigned int
+util_copy_fragsinfo_from_skb(unsigned char *calling_ctx, void *skb_in,
+                            unsigned int firstfraglen, unsigned int frags_max,
+                            struct phys_info frags[])
+{
+       unsigned int count = 0, ii, size, offset = 0, numfrags;
+       struct sk_buff *skb = skb_in;
+
+       numfrags = skb_shinfo(skb)->nr_frags;
+
+       while (firstfraglen) {
+               if (count == frags_max) {
+                       LOGERR("%s frags array too small: max:%d count:%d\n",
+                              calling_ctx, frags_max, count);
+                       return -1;      /* failure */
+               }
+               frags[count].pi_pfn =
+                   page_to_pfn(virt_to_page(skb->data + offset));
+               frags[count].pi_off =
+                   (unsigned long) (skb->data + offset) & PI_PAGE_MASK;
+               size =
+                   min(firstfraglen,
+                       (unsigned int) (PI_PAGE_SIZE - frags[count].pi_off));
+               /* can take smallest of firstfraglen(what's left) OR
+               * bytes left in the page
+               */
+               frags[count].pi_len = size;
+               firstfraglen -= size;
+               offset += size;
+               count++;
+       }
+       if (numfrags) {
+               if ((count + numfrags) > frags_max) {
+                       LOGERR("**** FAILED %s frags array too small: max:%d count+nr_frags:%d\n",
+                            calling_ctx, frags_max, count + numfrags);
+                       return -1;      /* failure */
+               }
+
+               for (ii = 0; ii < numfrags; ii++) {
+                       count = add_physinfo_entries(page_to_pfn(skb_frag_page(&skb_shinfo(skb)->frags[ii])),   /* pfn */
+                                                    skb_shinfo(skb)->frags[ii].
+                                                    page_offset,
+                                                    skb_shinfo(skb)->frags[ii].
+                                                    size, count, frags_max,
+                                                    frags);
+                       if (count == 0) {
+                               LOGERR("**** FAILED to add physinfo entries\n");
+                               return -1;      /* failure */
+                       }
+               }
+       }
+       if (skb_shinfo(skb)->frag_list) {
+               struct sk_buff *skbinlist;
+               int c;
+               for (skbinlist = skb_shinfo(skb)->frag_list; skbinlist;
+                    skbinlist = skbinlist->next) {
+
+                       c = util_copy_fragsinfo_from_skb("recursive", skbinlist,
+                                                        skbinlist->len -
+                                                        skbinlist->data_len,
+                                                        frags_max - count,
+                                                        &frags[count]);
+                       if (c == -1) {
+                               LOGERR("**** FAILED recursive call failed\n");
+                               return -1;
+                       }
+                       count += c;
+               }
+       }
+       return count;
+}
+EXPORT_SYMBOL_GPL(util_copy_fragsinfo_from_skb);
+
+static LIST_HEAD(ReqHandlerInfo_list); /* list of ReqHandlerInfo_t */
+static DEFINE_SPINLOCK(ReqHandlerInfo_list_lock);
+
+ReqHandlerInfo_t *
+ReqHandlerAdd(GUID switchTypeGuid,
+             const char *switch_type_name,
+             int (*controlfunc)(struct io_msgs *),
+             unsigned long min_channel_bytes,
+             int (*Server_Channel_Ok)(unsigned long channelBytes),
+             int (*Server_Channel_Init)
+              (void *x, unsigned char *clientStr, U32 clientStrLen, U64 bytes))
+{
+       ReqHandlerInfo_t *rc = NULL;
+
+       rc = UISMALLOC(sizeof(*rc), GFP_ATOMIC);
+       if (!rc)
+               return NULL;
+       memset(rc, 0, sizeof(*rc));
+       rc->switchTypeGuid = switchTypeGuid;
+       rc->controlfunc = controlfunc;
+       rc->min_channel_bytes = min_channel_bytes;
+       rc->Server_Channel_Ok = Server_Channel_Ok;
+       rc->Server_Channel_Init = Server_Channel_Init;
+       if (switch_type_name)
+               strncpy(rc->switch_type_name, switch_type_name,
+                       sizeof(rc->switch_type_name) - 1);
+       spin_lock(&ReqHandlerInfo_list_lock);
+       list_add_tail(&(rc->list_link), &ReqHandlerInfo_list);
+       spin_unlock(&ReqHandlerInfo_list_lock);
+
+       return rc;
+}
+
+ReqHandlerInfo_t *
+ReqHandlerFind(GUID switchTypeGuid)
+{
+       struct list_head *lelt, *tmp;
+       ReqHandlerInfo_t *entry = NULL;
+       spin_lock(&ReqHandlerInfo_list_lock);
+       list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) {
+               entry = list_entry(lelt, ReqHandlerInfo_t, list_link);
+               if (memcmp
+                   (&entry->switchTypeGuid, &switchTypeGuid,
+                    sizeof(GUID)) == 0) {
+                       spin_unlock(&ReqHandlerInfo_list_lock);
+                       return entry;
+               }
+       }
+       spin_unlock(&ReqHandlerInfo_list_lock);
+       return NULL;
+}
+
+int
+ReqHandlerDel(GUID switchTypeGuid)
+{
+       struct list_head *lelt, *tmp;
+       ReqHandlerInfo_t *entry = NULL;
+       int rc = -1;
+       spin_lock(&ReqHandlerInfo_list_lock);
+       list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) {
+               entry = list_entry(lelt, ReqHandlerInfo_t, list_link);
+               if (memcmp
+                   (&entry->switchTypeGuid, &switchTypeGuid,
+                    sizeof(GUID)) == 0) {
+                       list_del(lelt);
+                       UISFREE(entry, sizeof(*entry));
+                       rc++;
+               }
+       }
+       spin_unlock(&ReqHandlerInfo_list_lock);
+       return rc;
+}