[patch V2 5/8] staging: visoruislib driver used to handle requests from virtpci

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The visoruislib module is a support library, used to handle requests
from virtpci.

Signed-off-by: Ken Cox <jkc@xxxxxxxxxx>
Cc: Ben Romer <sparmaintainer@xxxxxxxxxx>

Index: upstream-staging/drivers/staging/unisys/Kconfig
===================================================================
--- upstream-staging.orig/drivers/staging/unisys/Kconfig
+++ upstream-staging/drivers/staging/unisys/Kconfig
@@ -13,5 +13,6 @@ source "drivers/staging/unisys/visorutil
 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: upstream-staging/drivers/staging/unisys/Makefile
===================================================================
--- upstream-staging.orig/drivers/staging/unisys/Makefile
+++ upstream-staging/drivers/staging/unisys/Makefile
@@ -5,4 +5,4 @@ obj-$(CONFIG_UNISYS_VISORUTIL)		+= visor
 obj-$(CONFIG_UNISYS_VISORCHANNEL)	+= visorchannel/
 obj-$(CONFIG_UNISYS_VISORCHIPSET)	+= visorchipset/
 obj-$(CONFIG_UNISYS_CHANNELSTUB)	+= channels/
-
+obj-$(CONFIG_UNISYS_UISLIB)		+= uislib/
Index: upstream-staging/drivers/staging/unisys/include/sparstop.h
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/include/sparstop.h
@@ -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
Index: upstream-staging/drivers/staging/unisys/uislib/Kconfig
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/uislib/Kconfig
@@ -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.
+
Index: upstream-staging/drivers/staging/unisys/uislib/Makefile
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/uislib/Makefile
@@ -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
+
Index: upstream-staging/drivers/staging/unisys/uislib/uislib.c
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/uislib/uislib.c
@@ -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 */
Index: upstream-staging/drivers/staging/unisys/uislib/uisqueue.c
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/uislib/uisqueue.c
@@ -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);
Index: upstream-staging/drivers/staging/unisys/uislib/uisthread.c
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/uislib/uisthread.c
@@ -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);
Index: upstream-staging/drivers/staging/unisys/uislib/uisutils.c
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/uislib/uisutils.c
@@ -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;
+}

_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux