[patch V2 6/8] staging: virtpci driver

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

 



The virtpci module handles the bus functions for virthba, and virtnic.

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
@@ -14,5 +14,6 @@ source "drivers/staging/unisys/visorchan
 source "drivers/staging/unisys/visorchipset/Kconfig"
 source "drivers/staging/unisys/channels/Kconfig"
 source "drivers/staging/unisys/uislib/Kconfig"
+source "drivers/staging/unisys/virtpci/Kconfig"
 
 endif # UNISYSSPAR
Index: upstream-staging/drivers/staging/unisys/Makefile
===================================================================
--- upstream-staging.orig/drivers/staging/unisys/Makefile
+++ upstream-staging/drivers/staging/unisys/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_UNISYS_VISORCHANNEL)	+= vis
 obj-$(CONFIG_UNISYS_VISORCHIPSET)	+= visorchipset/
 obj-$(CONFIG_UNISYS_CHANNELSTUB)	+= channels/
 obj-$(CONFIG_UNISYS_UISLIB)		+= uislib/
+obj-$(CONFIG_UNISYS_VIRTPCI)		+= virtpci/
Index: upstream-staging/drivers/staging/unisys/virtpci/Kconfig
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/virtpci/Kconfig
@@ -0,0 +1,10 @@
+#
+# Unisys virtpci configuration
+#
+
+config UNISYS_VIRTPCI
+	tristate "Unisys virtpci driver"
+	depends on UNISYSSPAR && UNISYS_UISLIB
+	---help---
+	If you say Y here, you will enable the Unisys virtpci driver.
+
Index: upstream-staging/drivers/staging/unisys/virtpci/Makefile
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/virtpci/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for Unisys virtpci
+#
+
+obj-$(CONFIG_UNISYS_VIRTPCI)	+= virtpci.o
+
+ccflags-y += -Idrivers/staging/unisys/include
+ccflags-y += -Idrivers/staging/unisys/uislib
+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/virtpci/virtpci.c
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/virtpci/virtpci.c
@@ -0,0 +1,1755 @@
+/* virtpci.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.
+ */
+
+#define EXPORT_SYMTAB
+
+#include <linux/kernel.h>
+#ifdef CONFIG_MODVERSIONS
+#include <config/modversions.h>
+#endif
+#include "uniklog.h"
+#include "diagnostics/appos_subsystems.h"
+#include "uisutils.h"
+#include "commontypes.h"
+#include "vbuschannel.h"
+#include "vbushelper.h"
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
+#include <linux/proc_fs.h>
+#include <linux/if_ether.h>
+#include <linux/version.h>
+#include "version.h"
+#include "guestlinuxdebug.h"
+
+struct driver_private {
+	struct kobject kobj;
+	struct klist klist_devices;
+	struct klist_node knode_bus;
+	struct module_kobject *mkobj;
+	struct device_driver *driver;
+};
+#define to_driver(obj) container_of(obj, struct driver_private, kobj)
+
+/* bus_id went away in 2.6.30 - the size was 20 bytes, so we'll define
+ * it ourselves, and a macro to make getting the field a bit simpler.
+ */
+#ifndef BUS_ID_SIZE
+#define BUS_ID_SIZE 20
+#endif
+
+#define BUS_ID(x) dev_name(x)
+
+#include "virtpci.h"
+
+/* this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages
+ */
+#define CURRENT_FILE_PC VIRT_PCI_PC_virtpci_c
+#define __MYFILE__ "virtpci.c"
+
+#define VIRTPCI_VERSION "01.00"
+
+/*****************************************************/
+/* Forward declarations                              */
+/*****************************************************/
+
+static int delete_vbus_device(struct device *vbus, void *data);
+static int match_busid(struct device *dev, void *data);
+static void virtpci_bus_release(struct device *dev);
+static void virtpci_device_release(struct device *dev);
+static int virtpci_device_add(struct device *parentbus, int devtype,
+			      struct add_virt_guestpart *addparams,
+			      struct scsi_adap_info *scsi,
+			      struct net_adap_info *net);
+static int virtpci_device_del(struct device *parentbus, int devtype,
+			      struct vhba_wwnn *wwnn, unsigned char macaddr[]);
+static int virtpci_device_serverdown(struct device *parentbus, int devtype,
+				     struct vhba_wwnn *wwnn,
+				     unsigned char macaddr[]);
+static int virtpci_device_serverup(struct device *parentbus, int devtype,
+				   struct vhba_wwnn *wwnn,
+				   unsigned char macaddr[]);
+static ssize_t virtpci_driver_attr_show(struct kobject *kobj,
+					struct attribute *attr, char *buf);
+static ssize_t virtpci_driver_attr_store(struct kobject *kobj,
+					 struct attribute *attr,
+					 const char *buf, size_t count);
+static int virtpci_bus_match(struct device *dev, struct device_driver *drv);
+static int virtpci_uevent(struct device *dev, struct kobj_uevent_env *env);
+static int virtpci_device_suspend(struct device *dev, pm_message_t state);
+static int virtpci_device_resume(struct device *dev);
+static int virtpci_device_probe(struct device *dev);
+static int virtpci_device_remove(struct device *dev);
+static ssize_t virt_proc_write(struct file *file, const char __user *buffer,
+			       size_t count, loff_t *ppos);
+static ssize_t info_proc_read(struct file *file, char __user *buf,
+			      size_t len, loff_t *offset);
+
+static const struct file_operations proc_virt_fops = {
+	.write = virt_proc_write,
+};
+
+static const struct file_operations proc_info_fops = {
+	.read = info_proc_read,
+};
+
+/*****************************************************/
+/* Globals                                           */
+/*****************************************************/
+
+/* methods in bus_type struct allow the bus code to serve as an
+ * intermediary between the device core and individual device core and
+ * individual drivers
+ */
+static struct bus_type virtpci_bus_type = {
+	.name = "uisvirtpci",
+	.match = virtpci_bus_match,
+	.uevent = virtpci_uevent,
+	.suspend = virtpci_device_suspend,
+	.resume = virtpci_device_resume,
+};
+
+static struct device virtpci_rootbus_device = {
+	.init_name = "vbusroot",	/* root bus */
+	.release = virtpci_bus_release
+};
+
+/* filled in with info about parent chipset driver when we register with it */
+static ULTRA_VBUS_DEVICEINFO Chipset_DriverInfo;
+
+static const struct sysfs_ops virtpci_driver_sysfs_ops = {
+	.show = virtpci_driver_attr_show,
+	.store = virtpci_driver_attr_store,
+};
+
+static struct kobj_type virtpci_driver_kobj_type = {
+	.sysfs_ops = &virtpci_driver_sysfs_ops,
+};
+
+static struct virtpci_dev *VpcidevListHead;
+static DEFINE_RWLOCK(VpcidevListLock);
+
+/* filled in with info about this driver, wrt it servicing client busses */
+static ULTRA_VBUS_DEVICEINFO Bus_DriverInfo;
+
+/* virtpci_proc_dir_entry is used to create the proc entry directory
+ * for virtpci
+ */
+static struct proc_dir_entry *virtpci_proc_dir;
+/* virt_proc_entry is used to tell virtpci to add/delete vhbas/vnics/vbuses */
+static struct proc_dir_entry *virt_proc_entry;
+/* info_proc_entry is used to tell virtpci to display current info
+ * kept in the driver
+ */
+static struct proc_dir_entry *info_proc_entry;
+#define VIRT_PROC_ENTRY_FN "virt"
+#define INFO_PROC_ENTRY_FN "info"
+#define DIR_PROC_ENTRY "virtpci"
+
+struct virtpci_busdev {
+	struct device virtpci_bus_device;
+};
+
+/*****************************************************/
+/* Local functions                                   */
+/*****************************************************/
+
+static inline int WAIT_FOR_IO_CHANNEL(ULTRA_IO_CHANNEL_PROTOCOL *chanptr)
+{
+	int count = 120;
+	while (count > 0) {
+
+		if (ULTRA_CHANNEL_SERVER_READY(&chanptr->ChannelHeader))
+			return 1;
+		UIS_THREAD_WAIT_SEC(1);
+		count--;
+	}
+	return 0;
+}
+
+/* Write the contents of <info> to the ULTRA_VBUS_CHANNEL_PROTOCOL.ChpInfo. */
+static int write_vbus_chpInfo(ULTRA_VBUS_CHANNEL_PROTOCOL *chan,
+			      ULTRA_VBUS_DEVICEINFO *info)
+{
+	int off;
+	if (!chan) {
+		LOGERR("vbus channel not present");
+		return -1;
+	}
+	off = sizeof(ULTRA_CHANNEL_PROTOCOL) + chan->HdrInfo.chpInfoByteOffset;
+	if (chan->HdrInfo.chpInfoByteOffset == 0) {
+		LOGERR("vbus channel not used, because chpInfoByteOffset == 0");
+		return -1;
+	}
+	memcpy(((U8 *) (chan)) + off, info, sizeof(*info));
+	return 0;
+}
+
+/* Write the contents of <info> to the ULTRA_VBUS_CHANNEL_PROTOCOL.BusInfo. */
+static int write_vbus_busInfo(ULTRA_VBUS_CHANNEL_PROTOCOL *chan,
+			      ULTRA_VBUS_DEVICEINFO *info)
+{
+	int off;
+	if (!chan) {
+		LOGERR("vbus channel not present");
+		return -1;
+	}
+	off = sizeof(ULTRA_CHANNEL_PROTOCOL) + chan->HdrInfo.busInfoByteOffset;
+	if (chan->HdrInfo.busInfoByteOffset == 0) {
+		LOGERR("vbus channel not used, because busInfoByteOffset == 0");
+		return -1;
+	}
+	memcpy(((U8 *) (chan)) + off, info, sizeof(*info));
+	return 0;
+}
+
+/* Write the contents of <info> to the
+ * ULTRA_VBUS_CHANNEL_PROTOCOL.DevInfo[<devix>].
+ */
+static int
+write_vbus_devInfo(ULTRA_VBUS_CHANNEL_PROTOCOL *chan,
+		   ULTRA_VBUS_DEVICEINFO *info, int devix)
+{
+	int off;
+	if (!chan) {
+		LOGERR("vbus channel not present");
+		return -1;
+	}
+	off =
+	    (sizeof(ULTRA_CHANNEL_PROTOCOL) +
+	     chan->HdrInfo.devInfoByteOffset) +
+	    (chan->HdrInfo.deviceInfoStructBytes * devix);
+	if (chan->HdrInfo.devInfoByteOffset == 0) {
+		LOGERR("vbus channel not used, because devInfoByteOffset == 0");
+		return -1;
+	}
+	memcpy(((U8 *) (chan)) + off, info, sizeof(*info));
+	return 0;
+}
+
+/* adds a vbus
+ * returns 0 failure, 1 success,
+ */
+static int add_vbus(struct add_vbus_guestpart *addparams)
+{
+	int ret;
+	struct device *vbus;
+	vbus = kmalloc(sizeof(struct device), GFP_ATOMIC);
+
+	POSTCODE_LINUX_2(VPCI_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+	if (!vbus)
+		return 0;
+
+	memset(vbus, 0, sizeof(struct device));
+	dev_set_name(vbus, "vbus%d", addparams->busNo);
+	vbus->release = virtpci_bus_release;
+	vbus->parent = &virtpci_rootbus_device;	/* root bus is parent */
+	vbus->bus = &virtpci_bus_type;	/* bus type */
+	vbus->platform_data = addparams->chanptr;
+
+	/* register a virt bus device -
+	 * this bus shows up under /sys/devices with .name value
+	 * "virtpci%d" any devices added to this bus then show up under
+	 * /sys/devices/virtpci0
+	 */
+	ret = device_register(vbus);
+	if (ret) {
+		LOGERR("device_register FAILED:%d\n", ret);
+		POSTCODE_LINUX_2(VPCI_CREATE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+		return 0;
+	}
+	write_vbus_chpInfo(vbus->platform_data /* chanptr */ ,
+			   &Chipset_DriverInfo);
+	write_vbus_busInfo(vbus->platform_data /* chanptr */ , &Bus_DriverInfo);
+	LOGINF("Added vbus %d; device %s created successfully\n",
+	       addparams->busNo, BUS_ID(vbus));
+	POSTCODE_LINUX_2(VPCI_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO);
+	return 1;
+}
+
+/* for CHANSOCK wwwnn/max are AUTO-GENERATED; for normal channels,
+ * wwnn/max are in the channel header.
+ */
+#define GET_SCSIADAPINFO_FROM_CHANPTR(chanptr) { \
+	scsi.wwnn = ((ULTRA_IO_CHANNEL_PROTOCOL *) chanptr)->vhba.wwnn; \
+	scsi.max = ((ULTRA_IO_CHANNEL_PROTOCOL *) chanptr)->vhba.max; \
+}
+
+/* find bus device with the busid that matches - match_busid matches bus_id */
+#define GET_BUS_DEV(busno) { \
+	sprintf(busid, "vbus%d", busno); \
+	vbus = bus_find_device(&virtpci_bus_type, NULL, \
+			       (void *)busid, match_busid);	\
+	if (!vbus) { \
+		LOGERR("**** FAILED to find vbus %s\n", busid); \
+		return 0; \
+	} \
+}
+
+/* adds a vhba
+ * returns 0 failure, 1 success,
+ */
+static int add_vhba(struct add_virt_guestpart *addparams)
+{
+	int i;
+	struct scsi_adap_info scsi;
+	struct device *vbus;
+	unsigned char busid[BUS_ID_SIZE];
+
+	POSTCODE_LINUX_2(VPCI_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+	if (!WAIT_FOR_IO_CHANNEL
+	    ((ULTRA_IO_CHANNEL_PROTOCOL *) addparams->chanptr)) {
+		LOGERR("Timed out.  Channel not ready\n");
+		POSTCODE_LINUX_2(VPCI_CREATE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+		return 0;
+	}
+
+	GET_SCSIADAPINFO_FROM_CHANPTR(addparams->chanptr);
+
+	GET_BUS_DEV(addparams->busNo);
+
+	LOGINF("Adding vhba wwnn:%x:%x config:%d-%d-%d-%d chanptr:%p\n",
+	       scsi.wwnn.wwnn1, scsi.wwnn.wwnn2,
+	       scsi.max.max_channel, scsi.max.max_id, scsi.max.max_lun,
+	       scsi.max.cmd_per_lun, addparams->chanptr);
+	i = virtpci_device_add(vbus, VIRTHBA_TYPE, addparams, &scsi, NULL);
+	if (i) {
+		LOGINF("Added vhba wwnn:%x:%x chanptr:%p\n", scsi.wwnn.wwnn1,
+		       scsi.wwnn.wwnn2, addparams->chanptr);
+		POSTCODE_LINUX_3(VPCI_CREATE_EXIT_PC, i,
+				 POSTCODE_SEVERITY_INFO);
+	}
+	return i;
+
+}
+
+/* for CHANSOCK macaddr is AUTO-GENERATED; for normal channels,
+ * macaddr is in the channel header.
+ */
+#define GET_NETADAPINFO_FROM_CHANPTR(chanptr) { \
+	memcpy(net.mac_addr, ((ULTRA_IO_CHANNEL_PROTOCOL *) chanptr)->vnic.macaddr, MAX_MACADDR_LEN); \
+	net.num_rcv_bufs = ((ULTRA_IO_CHANNEL_PROTOCOL *) chanptr)->vnic.num_rcv_bufs; \
+	net.mtu = ((ULTRA_IO_CHANNEL_PROTOCOL *) chanptr)->vnic.mtu; \
+	net.zoneGuid = ((ULTRA_IO_CHANNEL_PROTOCOL *) chanptr)->vnic.zoneGuid; \
+}
+
+/* adds a vnic
+ * returns 0 failure, 1 success,
+ */
+static int
+add_vnic(struct add_virt_guestpart *addparams)
+{
+	int i;
+	struct net_adap_info net;
+	struct device *vbus;
+	unsigned char busid[BUS_ID_SIZE];
+
+	POSTCODE_LINUX_2(VPCI_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+	if (!WAIT_FOR_IO_CHANNEL
+	    ((ULTRA_IO_CHANNEL_PROTOCOL *) addparams->chanptr)) {
+		LOGERR("Timed out, channel not ready\n");
+		POSTCODE_LINUX_2(VPCI_CREATE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+		return 0;
+	}
+
+	GET_NETADAPINFO_FROM_CHANPTR(addparams->chanptr);
+
+	GET_BUS_DEV(addparams->busNo);
+
+	LOGINF("Adding vnic macaddr:%02x:%02x:%02x:%02x:%02x:%02x rcvbufs:%d mtu:%d chanptr:%p{%-8.8lx-%-4.4x-%-4.4x-%-2.2x%-2.2x%-2.2x%-2.2x%-2.2x%-2.2x%-2.2x%-2.2x}\n",
+	     net.mac_addr[0], net.mac_addr[1], net.mac_addr[2], net.mac_addr[3],
+	     net.mac_addr[4], net.mac_addr[5], net.num_rcv_bufs, net.mtu,
+	     addparams->chanptr, (ulong) net.zoneGuid.data1, net.zoneGuid.data2,
+	     net.zoneGuid.data3, net.zoneGuid.data4[0], net.zoneGuid.data4[1],
+	     net.zoneGuid.data4[2], net.zoneGuid.data4[3],
+	     net.zoneGuid.data4[4], net.zoneGuid.data4[5],
+	     net.zoneGuid.data4[6], net.zoneGuid.data4[7]);
+	i = virtpci_device_add(vbus, VIRTNIC_TYPE, addparams, NULL, &net);
+	if (i) {
+		LOGINF("Added vnic macaddr:%02x:%02x:%02x:%02x:%02x:%02x\n",
+		       net.mac_addr[0], net.mac_addr[1], net.mac_addr[2],
+		       net.mac_addr[3], net.mac_addr[4], net.mac_addr[5]);
+		POSTCODE_LINUX_3(VPCI_CREATE_EXIT_PC, i,
+				 POSTCODE_SEVERITY_INFO);
+		return 1;
+	}
+	return 0;
+}
+
+/* delete vbus
+ * returns 0 failure, 1 success,
+ */
+static int
+delete_vbus(struct del_vbus_guestpart *delparams)
+{
+	struct device *vbus;
+	unsigned char busid[BUS_ID_SIZE];
+
+	GET_BUS_DEV(delparams->busNo);
+	/* ensure that bus has no devices? -- TBD */
+	LOGINF("Deleting %s\n", BUS_ID(vbus));
+	if (delete_vbus_device(vbus, NULL))
+		return 0;	/* failure */
+	LOGINF("Deleted vbus %d\n", delparams->busNo);
+	return 1;
+}
+
+static int
+delete_vbus_device(struct device *vbus, void *data)
+{
+	int checkforroot = (data != NULL);
+	struct device *pDev = &virtpci_rootbus_device;
+
+	if ((checkforroot) && match_busid(vbus, (void *) BUS_ID(pDev))) {
+		/* skip it - don't delete root bus */
+		LOGINF("skipping root bus\n");
+		return 0;	/* pretend no error */
+	}
+	LOGINF("Calling unregister for %s\n", BUS_ID(vbus));
+	device_unregister(vbus);
+	kfree(vbus);
+	LOGINF("VBus unregister and freed\n");
+	return 0;		/* no error */
+}
+
+/* pause vhba
+* returns 0 failure, 1 success,
+*/
+static int pause_vhba(struct pause_virt_guestpart *pauseparams)
+{
+	int i;
+	struct scsi_adap_info scsi;
+
+	GET_SCSIADAPINFO_FROM_CHANPTR(pauseparams->chanptr);
+
+	LOGINF("Pausing vhba wwnn:%x:%x\n", scsi.wwnn.wwnn1, scsi.wwnn.wwnn2);
+	i = virtpci_device_serverdown(NULL /*no parent bus */ , VIRTHBA_TYPE,
+				      &scsi.wwnn, NULL);
+	if (i)
+		LOGINF("Paused vhba wwnn:%x:%x\n", scsi.wwnn.wwnn1,
+		       scsi.wwnn.wwnn2);
+	return i;
+}
+
+/* pause vnic
+ * returns 0 failure, 1 success,
+ */
+static int pause_vnic(struct pause_virt_guestpart *pauseparams)
+{
+	int i;
+	struct net_adap_info net;
+
+	GET_NETADAPINFO_FROM_CHANPTR(pauseparams->chanptr);
+
+	LOGINF("Pausing vnic macaddr:%02x:%02x:%02x:%02x:%02x:%02x\n",
+	       net.mac_addr[0], net.mac_addr[1], net.mac_addr[2],
+	       net.mac_addr[3], net.mac_addr[4], net.mac_addr[5]);
+	i = virtpci_device_serverdown(NULL /*no parent bus */ , VIRTNIC_TYPE,
+				      NULL, net.mac_addr);
+	if (i) {
+		LOGINF(" Paused vnic macaddr:%02x:%02x:%02x:%02x:%02x:%02x\n",
+		       net.mac_addr[0], net.mac_addr[1], net.mac_addr[2],
+		       net.mac_addr[3], net.mac_addr[4], net.mac_addr[5]);
+	}
+	return i;
+}
+
+/* resume vhba
+ * returns 0 failure, 1 success,
+ */
+static int resume_vhba(struct resume_virt_guestpart *resumeparams)
+{
+	int i;
+	struct scsi_adap_info scsi;
+
+	GET_SCSIADAPINFO_FROM_CHANPTR(resumeparams->chanptr);
+
+	LOGINF("Resuming vhba wwnn:%x:%x\n", scsi.wwnn.wwnn1, scsi.wwnn.wwnn2);
+	i = virtpci_device_serverup(NULL /*no parent bus */ , VIRTHBA_TYPE,
+				    &scsi.wwnn, NULL);
+	if (i)
+		LOGINF("Resumed vhba wwnn:%x:%x\n", scsi.wwnn.wwnn1,
+		       scsi.wwnn.wwnn2);
+	return i;
+}
+
+/* resume vnic
+* returns 0 failure, 1 success,
+*/
+static int
+resume_vnic(struct resume_virt_guestpart *resumeparams)
+{
+	int i;
+	struct net_adap_info net;
+
+	GET_NETADAPINFO_FROM_CHANPTR(resumeparams->chanptr);
+
+	LOGINF("Resuming vnic macaddr:%02x:%02x:%02x:%02x:%02x:%02x\n",
+	       net.mac_addr[0], net.mac_addr[1], net.mac_addr[2],
+	       net.mac_addr[3], net.mac_addr[4], net.mac_addr[5]);
+	i = virtpci_device_serverup(NULL /*no parent bus */ , VIRTNIC_TYPE,
+				    NULL, net.mac_addr);
+	if (i) {
+		LOGINF(" Resumed vnic macaddr:%02x:%02x:%02x:%02x:%02x:%02x\n",
+		       net.mac_addr[0], net.mac_addr[1], net.mac_addr[2],
+		       net.mac_addr[3], net.mac_addr[4], net.mac_addr[5]);
+	}
+	return i;
+}
+
+/* delete vhba
+* returns 0 failure, 1 success,
+*/
+static int delete_vhba(struct del_virt_guestpart *delparams)
+{
+	int i;
+	struct scsi_adap_info scsi;
+
+	GET_SCSIADAPINFO_FROM_CHANPTR(delparams->chanptr);
+
+	LOGINF("Deleting vhba wwnn:%x:%x\n", scsi.wwnn.wwnn1, scsi.wwnn.wwnn2);
+	i = virtpci_device_del(NULL /*no parent bus */ , VIRTHBA_TYPE,
+			       &scsi.wwnn, NULL);
+	if (i) {
+		LOGINF("Deleted vhba wwnn:%x:%x\n", scsi.wwnn.wwnn1,
+		       scsi.wwnn.wwnn2);
+		return 1;
+	}
+	return 0;
+}
+
+/* deletes a vnic
+ * returns 0 failure, 1 success,
+ */
+static int delete_vnic(struct del_virt_guestpart *delparams)
+{
+	int i;
+	struct net_adap_info net;
+
+	GET_NETADAPINFO_FROM_CHANPTR(delparams->chanptr);
+
+	LOGINF("Deleting vnic macaddr:%02x:%02x:%02x:%02x:%02x:%02x\n",
+	       net.mac_addr[0], net.mac_addr[1], net.mac_addr[2],
+	       net.mac_addr[3], net.mac_addr[4], net.mac_addr[5]);
+	i = virtpci_device_del(NULL /*no parent bus */ , VIRTNIC_TYPE, NULL,
+			       net.mac_addr);
+	if (i) {
+		LOGINF("Deleted vnic macaddr:%02x:%02x:%02x:%02x:%02x:%02x\n",
+		       net.mac_addr[0], net.mac_addr[1], net.mac_addr[2],
+		       net.mac_addr[3], net.mac_addr[4], net.mac_addr[5]);
+	}
+	return i;
+}
+
+#define DELETE_ONE_VPCIDEV(vpcidev) { \
+	LOGINF("calling device_unregister:%p\n", &vpcidev->generic_dev); \
+	device_unregister(&vpcidev->generic_dev); \
+	LOGINF("Deleted %p\n", vpcidev); \
+	kfree(vpcidev); \
+}
+
+/* deletes all vhbas and vnics
+ * returns 0 failure, 1 success,
+ */
+static void delete_all(void)
+{
+	int count = 0;
+	unsigned long flags;
+	struct virtpci_dev *tmpvpcidev, *nextvpcidev;
+
+	/* delete the entire vhba/vnic list in one shot */
+	write_lock_irqsave(&VpcidevListLock, flags);
+	tmpvpcidev = VpcidevListHead;
+	VpcidevListHead = NULL;
+	write_unlock_irqrestore(&VpcidevListLock, flags);
+
+	/* delete one vhba/vnic at a time */
+	while (tmpvpcidev) {
+		nextvpcidev = tmpvpcidev->next;
+		/* delete the vhba/vnic at tmpvpcidev */
+		DELETE_ONE_VPCIDEV(tmpvpcidev);
+		tmpvpcidev = nextvpcidev;
+		count++;
+	}
+	LOGINF("Deleted %d vhbas/vnics.\n", count);
+
+	/* now delete each vbus */
+	if (bus_for_each_dev
+	    (&virtpci_bus_type, NULL, (void *) 1, delete_vbus_device))
+		LOGERR("delete of all vbus failed\n");
+}
+
+/* deletes all vnics or vhbas
+ * returns 0 failure, 1 success,
+ */
+static int delete_all_virt(VIRTPCI_DEV_TYPE devtype, struct del_vbus_guestpart *delparams)
+{
+	int i;
+	unsigned char busid[BUS_ID_SIZE];
+	struct device *vbus;
+
+	GET_BUS_DEV(delparams->busNo);
+
+	if ((devtype != VIRTHBA_TYPE) && (devtype != VIRTNIC_TYPE)) {
+		LOGERR("**** FAILED to delete all devices; devtype:%d not vhba:%d or vnic:%d\n",
+		     devtype, VIRTHBA_TYPE, VIRTNIC_TYPE);
+		return 0;
+	}
+
+	LOGINF("Deleting all %s in vbus %s\n",
+	       devtype == VIRTHBA_TYPE ? "vhbas" : "vnics", busid);
+	/* delete all vhbas/vnics */
+	i = virtpci_device_del(vbus, devtype, NULL, NULL);
+	if (i > 0)
+		LOGINF("Deleted %d %s\n", i,
+		       devtype == VIRTHBA_TYPE ? "vhbas" : "vnics");
+	return 1;
+}
+
+static int virtpci_ctrlchan_func(struct guest_msgs *msg)
+{
+	switch (msg->msgtype) {
+	case GUEST_ADD_VBUS:
+		return add_vbus(&msg->add_vbus);
+	case GUEST_ADD_VHBA:
+		return add_vhba(&msg->add_vhba);
+	case GUEST_ADD_VNIC:
+		return add_vnic(&msg->add_vnic);
+	case GUEST_DEL_VBUS:
+		return delete_vbus(&msg->del_vbus);
+	case GUEST_DEL_VHBA:
+		return delete_vhba(&msg->del_vhba);
+	case GUEST_DEL_VNIC:
+		return delete_vnic(&msg->del_vhba);
+	case GUEST_DEL_ALL_VHBAS:
+		return delete_all_virt(VIRTHBA_TYPE, &msg->del_all_vhbas);
+	case GUEST_DEL_ALL_VNICS:
+		return delete_all_virt(VIRTNIC_TYPE, &msg->del_all_vnics);
+	case GUEST_DEL_ALL_VBUSES:
+		delete_all();
+		return 1;
+	case GUEST_PAUSE_VHBA:
+		return pause_vhba(&msg->pause_vhba);
+	case GUEST_PAUSE_VNIC:
+		return pause_vnic(&msg->pause_vnic);
+	case GUEST_RESUME_VHBA:
+		return resume_vhba(&msg->resume_vhba);
+	case GUEST_RESUME_VNIC:
+		return resume_vnic(&msg->resume_vnic);
+	default:
+		LOGERR("invalid message type %d.\n", msg->msgtype);
+		return 0;
+	}
+}
+
+/* same as driver_helper in bus.c linux */
+static int match_busid(struct device *dev, void *data)
+{
+	const char *name = data;
+
+	if (strcmp(name, BUS_ID(dev)) == 0)
+		return 1;
+	return 0;
+}
+
+/*****************************************************/
+/*  Bus functions                                    */
+/*****************************************************/
+
+const struct pci_device_id *
+virtpci_match_device(const struct pci_device_id *ids,
+		     const struct virtpci_dev *dev)
+{
+	while (ids->vendor || ids->subvendor || ids->class_mask) {
+		DBGINF("ids->vendor:%x dev->vendor:%x ids->device:%x dev->device:%x\n",
+		     ids->vendor, dev->vendor, ids->device, dev->device);
+
+		if ((ids->vendor == dev->vendor)
+		    && (ids->device == dev->device))
+			return ids;
+
+		ids++;
+	}
+	return NULL;
+}
+
+/* NOTE: !!!!!!  This function is called when a new device is added
+* for this bus.  Or, it is called for existing devices when a new
+* driver is added for this bus.  It returns nonzero if a given device
+* can be handled by the given driver.
+*/
+static int virtpci_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct virtpci_dev *virtpcidev = device_to_virtpci_dev(dev);
+	struct virtpci_driver *virtpcidrv = driver_to_virtpci_driver(drv);
+	int match = 0;
+
+	DBGINF("In virtpci_bus_match dev->bus_id:%s drv->name:%s\n",
+	       dev->bus_id, drv->name);
+
+	/* check ids list for a match */
+	if (virtpci_match_device(virtpcidrv->id_table, virtpcidev))
+		match = 1;
+
+	DBGINF("returning match:%d\n", match);
+	return match;		/* 0 - no match; 1 - yes it matches */
+}
+
+static int virtpci_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	DBGINF("In virtpci_hotplug\n");
+	/* add variables to the environment prior to the generation of
+	 * hotplug events to user space
+	 */
+	if (add_uevent_var(env, "VIRTPCI_VERSION=%s", VIRTPCI_VERSION))
+		return -ENOMEM;
+	return 0;
+}
+
+static int virtpci_device_suspend(struct device *dev, pm_message_t state)
+{
+	DBGINF("In virtpci_device_suspend -NYI ****\n");
+	return 0;
+}
+
+static int virtpci_device_resume(struct device *dev)
+{
+	DBGINF("In virtpci_device_resume -NYI ****\n");
+	return 0;
+}
+
+/* For a child device just created on a client bus, fill in
+ * information about the driver that is controlling this device into
+ * the the appropriate slot within the vbus channel of the bus
+ * instance.
+ */
+static void fix_vbus_devInfo(struct device *dev, int devNo, int devType,
+			     struct virtpci_driver *virtpcidrv)
+{
+	struct device *vbus;
+	void *pChan;
+	ULTRA_VBUS_DEVICEINFO devInfo;
+	const char *stype;
+
+	if (!dev) {
+		LOGERR("%s dev is NULL", __func__);
+		return;
+	}
+	if (!virtpcidrv) {
+		LOGERR("%s driver is NULL", __func__);
+		return;
+	}
+	vbus = dev->parent;
+	if (!vbus) {
+		LOGERR("%s dev has no parent bus", __func__);
+		return;
+	}
+	pChan = vbus->platform_data;
+	if (!pChan) {
+		LOGERR("%s dev bus has no channel", __func__);
+		return;
+	}
+	switch (devType) {
+	case PCI_DEVICE_ID_VIRTHBA:
+		stype = "vHBA";
+		break;
+	case PCI_DEVICE_ID_VIRTNIC:
+		stype = "vNIC";
+		break;
+	default:
+		stype = "unknown";
+		break;
+	}
+	BusDeviceInfo_Init(&devInfo, stype,
+			   virtpcidrv->name,
+			   virtpcidrv->version,
+			   virtpcidrv->vertag,
+			   virtpcidrv->build_date, virtpcidrv->build_time);
+	write_vbus_devInfo(pChan, &devInfo, devNo);
+
+	/* Re-write bus+chipset info, because it is possible that this
+	* was previously written by our good counterpart, visorbus.
+	*/
+	write_vbus_chpInfo(pChan, &Chipset_DriverInfo);
+	write_vbus_busInfo(pChan, &Bus_DriverInfo);
+}
+
+/* This function is called to query the existence of a specific device
+* and whether this driver can work with it.  It should return -ENODEV
+* in case of failure.
+*/
+static int virtpci_device_probe(struct device *dev)
+{
+	struct virtpci_dev *virtpcidev = device_to_virtpci_dev(dev);
+	struct virtpci_driver *virtpcidrv =
+	    driver_to_virtpci_driver(dev->driver);
+	const struct pci_device_id *id;
+	int error = 0;
+
+	LOGINF("In virtpci_device_probe dev:%p virtpcidev:%p virtpcidrv:%p\n",
+	       dev, virtpcidev, virtpcidrv);	/* VERBOSE/DEBUG ? */
+	POSTCODE_LINUX_2(VPCI_PROBE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+	/* static match and static probe vs dynamic match & dynamic
+	 * probe - do we care?.
+	 */
+	if (!virtpcidrv->id_table)
+		return -ENODEV;
+
+	id = virtpci_match_device(virtpcidrv->id_table, virtpcidev);
+	if (!id)
+		return -ENODEV;
+
+	/* increment reference count */
+	get_device(dev);
+
+	/* if virtpcidev is not already claimed & probe function is
+	 * valid, probe it
+	 */
+	if (!virtpcidev->mydriver && virtpcidrv->probe) {
+		/* call the probe function - virthba or virtnic probe
+		 * is what it should be
+		 */
+		error = virtpcidrv->probe(virtpcidev, id);
+		if (!error) {
+			fix_vbus_devInfo(dev, virtpcidev->deviceNo,
+					 virtpcidev->device, virtpcidrv);
+			virtpcidev->mydriver = virtpcidrv;
+			POSTCODE_LINUX_2(VPCI_PROBE_EXIT_PC,
+					 POSTCODE_SEVERITY_INFO);
+		} else
+			put_device(dev);
+	}
+	POSTCODE_LINUX_2(VPCI_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+	return error;		/* -ENODEV for probe failure */
+}
+
+static int virtpci_device_remove(struct device *dev_)
+{
+	/* dev_ passed in is the HBA device which we called
+	* generic_dev in our virtpcidev struct
+	*/
+	struct virtpci_dev *virtpcidev = device_to_virtpci_dev(dev_);
+	struct virtpci_driver *virtpcidrv = virtpcidev->mydriver;
+	LOGINF("In virtpci_device_remove bus_id:%s dev_:%p virtpcidev:%p dev->driver:%p drivername:%s\n",
+	       BUS_ID(dev_), dev_, virtpcidev, dev_->driver,
+	       dev_->driver->name);	/* VERBOSE/DEBUG */
+	if (virtpcidrv) {
+		/* TEMP: assuming we have only one such driver for now */
+		if (virtpcidrv->remove)
+			virtpcidrv->remove(virtpcidev);
+		virtpcidev->mydriver = NULL;
+	}
+
+	DBGINF("calling putdevice\n");
+	put_device(dev_);
+
+	DBGINF("Leaving\n");
+	return 0;
+}
+
+/*****************************************************/
+/* Bus functions                                     */
+/*****************************************************/
+
+static void virtpci_bus_release(struct device *dev)
+{
+	/* this function is called when the last reference to the
+	 * device is removed
+	 */
+	DBGINF("In virtpci_bus_release\n");
+	/* what else is supposed to happen here? */
+}
+
+/*****************************************************/
+/* Adapter functions                                 */
+/*****************************************************/
+
+static int virtpci_device_add(struct device *parentbus, int devtype,
+			      struct add_virt_guestpart *addparams,
+			      struct scsi_adap_info *scsi, /* NULL for VNIC add */
+			      struct net_adap_info *net	/* NULL for VHBA add */)
+{
+	struct virtpci_dev *virtpcidev = NULL;
+	struct virtpci_dev *tmpvpcidev = NULL, *prev;
+	unsigned long flags;
+	int ret;
+	ULTRA_IO_CHANNEL_PROTOCOL *pIoChan = NULL;
+	struct device *pDev;
+
+	LOGINF("virtpci_device_add parentbus:%p chanptr:%p\n", parentbus,
+	       addparams->chanptr);
+
+	POSTCODE_LINUX_2(VPCI_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+
+	if ((devtype != VIRTHBA_TYPE) && (devtype != VIRTNIC_TYPE)) {
+		LOGERR("**** FAILED to add device; devtype:%d not vhba:%d or vnic:%d\n",
+		     devtype, VIRTHBA_TYPE, VIRTNIC_TYPE);
+		POSTCODE_LINUX_3(VPCI_CREATE_FAILURE_PC, devtype,
+				 POSTCODE_SEVERITY_ERR);
+		return 0;
+	}
+
+	/* add a Virtual Device */
+	virtpcidev = kmalloc(sizeof(struct virtpci_dev), GFP_ATOMIC);
+	if (virtpcidev == NULL) {
+		LOGERR("can't add device - malloc FALLED\n");
+		POSTCODE_LINUX_2(MALLOC_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+		return 0;
+	}
+
+	memset(virtpcidev, 0, sizeof(struct virtpci_dev));
+
+	/* initialize stuff unique to virtpci_dev struct */
+	virtpcidev->devtype = devtype;
+	if (devtype == VIRTHBA_TYPE) {
+		virtpcidev->device = PCI_DEVICE_ID_VIRTHBA;
+		virtpcidev->scsi = *scsi;
+	} else {
+		virtpcidev->device = PCI_DEVICE_ID_VIRTNIC;
+		virtpcidev->net = *net;
+	}
+	virtpcidev->vendor = PCI_VENDOR_ID_UNISYS;
+	virtpcidev->busNo = addparams->busNo;
+	virtpcidev->deviceNo = addparams->deviceNo;
+
+	virtpcidev->queueinfo.chan = addparams->chanptr;
+	virtpcidev->queueinfo.send_int_if_needed = NULL;
+
+	/* Set up safe queue... */
+	pIoChan = (ULTRA_IO_CHANNEL_PROTOCOL *) virtpcidev->queueinfo.chan;
+
+	virtpcidev->intr = addparams->intr;
+
+	/* initialize stuff in the device portion of the struct */
+	virtpcidev->generic_dev.bus = &virtpci_bus_type;
+	virtpcidev->generic_dev.parent = parentbus;
+	virtpcidev->generic_dev.release = virtpci_device_release;
+
+	dev_set_name(&virtpcidev->generic_dev, "%x:%x",
+		     addparams->busNo, addparams->deviceNo);
+
+	/* add the vhba/vnic to virtpci device list - but check for
+	 * duplicate wwnn/macaddr first
+	 */
+	write_lock_irqsave(&VpcidevListLock, flags);
+	for (tmpvpcidev = VpcidevListHead; tmpvpcidev;
+	     tmpvpcidev = tmpvpcidev->next) {
+		if (devtype == VIRTHBA_TYPE) {
+			if ((tmpvpcidev->scsi.wwnn.wwnn1 == scsi->wwnn.wwnn1) &&
+			    (tmpvpcidev->scsi.wwnn.wwnn2 == scsi->wwnn.wwnn2)) {
+				/* duplicate - already have vpcidev
+				   with this wwnn */
+				break;
+			}
+		} else
+		    if (memcmp
+			(tmpvpcidev->net.mac_addr, net->mac_addr,
+			 MAX_MACADDR_LEN) == 0) {
+			/* duplicate - already have vnic with this wwnn */
+			break;
+		}
+	}
+	if (tmpvpcidev) {
+		/* found a vhba/vnic already in the list with same
+		 * wwnn or macaddr - reject add
+		 */
+		write_unlock_irqrestore(&VpcidevListLock, flags);
+		kfree(virtpcidev);
+		LOGERR("**** FAILED vhba/vnic already exists in the list\n");
+		POSTCODE_LINUX_2(VPCI_CREATE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+		return 0;
+	}
+
+	/* add it at the head */
+	if (!VpcidevListHead)
+		VpcidevListHead = virtpcidev;
+	else {
+		/* insert virtpcidev at the head of our linked list of
+		 * vpcidevs
+		 */
+		virtpcidev->next = VpcidevListHead;
+		VpcidevListHead = virtpcidev;
+	}
+
+	write_unlock_irqrestore(&VpcidevListLock, flags);
+
+	/* Must transition channel to ATTACHED state BEFORE
+	 * registering the device, because polling of the channel
+	 * queues can begin at any time after device_register().
+	 */
+	pDev = &virtpcidev->generic_dev;
+	ULTRA_CHANNEL_CLIENT_TRANSITION(addparams->chanptr,
+					BUS_ID(pDev),
+					CliStateOS, CHANNELCLI_ATTACHED, NULL);
+
+	/* don't register until device has been added to
+	* list. Otherwise, a device_unregister from this function can
+	* cause a "scheduling while atomic".
+	*/
+	DBGINF("registering device:%p with bus_id:%s\n",
+	       &virtpcidev->generic_dev, virtpcidev->generic_dev.bus_id);
+	ret = device_register(&virtpcidev->generic_dev);
+	/* NOTE: THIS IS CALLING HOTPLUG virtpci_hotplug!!!
+	 * This call to device_register results in virtpci_bus_match
+	 * being called !!!!!  And, if match returns success, then
+	 * virtpcidev->generic_dev.driver is setup to core_driver,
+	 * i.e., virtpci and the probe function
+	 * virtpcidev->generic_dev.driver->probe is called which
+	 * results in virtpci_device_probe being called. And if
+	 * virtpci_device_probe is successful
+	 */
+	if (ret) {
+		LOGERR("device_register returned %d\n", ret);
+		pDev = &virtpcidev->generic_dev;
+		ULTRA_CHANNEL_CLIENT_TRANSITION(addparams->chanptr,
+						BUS_ID(pDev),
+						CliStateOS,
+						CHANNELCLI_DETACHED, NULL);
+		/* remove virtpcidev, the one we just added, from the list */
+		write_lock_irqsave(&VpcidevListLock, flags);
+		for (tmpvpcidev = VpcidevListHead, prev = NULL;
+		     tmpvpcidev;
+		     prev = tmpvpcidev, tmpvpcidev = tmpvpcidev->next) {
+			if (tmpvpcidev == virtpcidev) {
+				if (prev)
+					prev->next = tmpvpcidev->next;
+				else
+					VpcidevListHead = tmpvpcidev->next;
+				break;
+			}
+		}
+		write_unlock_irqrestore(&VpcidevListLock, flags);
+		kfree(virtpcidev);
+		return 0;
+	}
+
+	LOGINF("Added %s:%d:%d &virtpcidev->generic_dev:%p\n",
+	       (devtype == VIRTHBA_TYPE) ? "virthba" : "virtnic",
+	       addparams->busNo, addparams->deviceNo, &virtpcidev->generic_dev);
+	POSTCODE_LINUX_2(VPCI_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO);
+	return 1;
+}
+
+static int virtpci_device_serverdown(struct device *parentbus,
+				     int devtype,
+				     struct vhba_wwnn *wwnn,
+				     unsigned char macaddr[])
+{
+	int pausethisone = 0;
+	bool found = false;
+	struct virtpci_dev *tmpvpcidev, *prevvpcidev;
+	struct virtpci_driver *vpcidriver;
+	unsigned long flags;
+	int rc = 0;
+
+	if ((devtype != VIRTHBA_TYPE) && (devtype != VIRTNIC_TYPE)) {
+		LOGERR("**** FAILED to pause device; devtype:%d not vhba:%d or vnic:%d\n",
+		       devtype, VIRTHBA_TYPE, VIRTNIC_TYPE);
+		return 0;
+	}
+
+	/* find the vhba or vnic in virtpci device list */
+	write_lock_irqsave(&VpcidevListLock, flags);
+
+	for (tmpvpcidev = VpcidevListHead, prevvpcidev = NULL;
+	     (tmpvpcidev && !found);
+	     prevvpcidev = tmpvpcidev, tmpvpcidev = tmpvpcidev->next) {
+		if (tmpvpcidev->devtype != devtype)
+			continue;
+
+		if (devtype == VIRTHBA_TYPE) {
+			pausethisone =
+			    ((tmpvpcidev->scsi.wwnn.wwnn1 == wwnn->wwnn1) &&
+			     (tmpvpcidev->scsi.wwnn.wwnn2 == wwnn->wwnn2));
+			/* devtype is vhba, we're pausing vhba whose
+			* wwnn matches the current device's wwnn
+			*/
+		} else {	/* VIRTNIC_TYPE */
+			pausethisone =
+			    memcmp(tmpvpcidev->net.mac_addr, macaddr,
+				   MAX_MACADDR_LEN) == 0;
+			/* devtype is vnic, we're pausing vnic whose
+			* macaddr matches the current device's macaddr */
+		}
+
+		if (!pausethisone)
+			continue;
+
+		found = true;
+		vpcidriver = tmpvpcidev->mydriver;
+		rc = vpcidriver->suspend(tmpvpcidev, 0);
+	}
+	write_unlock_irqrestore(&VpcidevListLock, flags);
+
+	if (!found) {
+		LOGERR("**** FAILED to find vhba/vnic in the list\n");
+		return 0;
+	}
+
+	return rc;
+}
+
+static int virtpci_device_serverup(struct device *parentbus,
+				   int devtype,
+				   struct vhba_wwnn *wwnn,
+				   unsigned char macaddr[])
+{
+	int resumethisone = 0;
+	bool found = false;
+	struct virtpci_dev *tmpvpcidev, *prevvpcidev;
+	struct virtpci_driver *vpcidriver;
+	unsigned long flags;
+	int rc = 0;
+
+	if ((devtype != VIRTHBA_TYPE) && (devtype != VIRTNIC_TYPE)) {
+		LOGERR("**** FAILED to resume device; devtype:%d not vhba:%d or vnic:%d\n",
+		       devtype, VIRTHBA_TYPE, VIRTNIC_TYPE);
+		return 0;
+	}
+
+	/* find the vhba or vnic in virtpci device list */
+	write_lock_irqsave(&VpcidevListLock, flags);
+
+	for (tmpvpcidev = VpcidevListHead, prevvpcidev = NULL;
+	     (tmpvpcidev && !found);
+	     prevvpcidev = tmpvpcidev, tmpvpcidev = tmpvpcidev->next) {
+		if (tmpvpcidev->devtype != devtype)
+			continue;
+
+		if (devtype == VIRTHBA_TYPE) {
+			resumethisone =
+			    ((tmpvpcidev->scsi.wwnn.wwnn1 == wwnn->wwnn1) &&
+			     (tmpvpcidev->scsi.wwnn.wwnn2 == wwnn->wwnn2));
+			/* devtype is vhba, we're resuming vhba whose
+			* wwnn matches the current device's wwnn */
+		} else {	/* VIRTNIC_TYPE */
+			resumethisone =
+			    memcmp(tmpvpcidev->net.mac_addr, macaddr,
+				   MAX_MACADDR_LEN) == 0;
+			/* devtype is vnic, we're resuming vnic whose
+			* macaddr matches the current device's macaddr */
+		}
+
+		if (!resumethisone)
+			continue;
+
+		found = true;
+		vpcidriver = tmpvpcidev->mydriver;
+		/* This should be done at BUS resume time, but an
+		* existing problem prevents us from ever getting a bus
+		* resume...  This hack would fail to work should we
+		* ever have a bus that contains NO devices, since we
+		* would never even get here in that case.
+		*/
+		fix_vbus_devInfo(&tmpvpcidev->generic_dev, tmpvpcidev->deviceNo,
+				 tmpvpcidev->device, vpcidriver);
+		rc = vpcidriver->resume(tmpvpcidev);
+	}
+
+	write_unlock_irqrestore(&VpcidevListLock, flags);
+
+	if (!found) {
+		LOGERR("**** FAILED to find vhba/vnic in the list\n");
+		return 0;
+	}
+
+	return rc;
+}
+
+static int virtpci_device_del(struct device *parentbus,
+			      int devtype, struct vhba_wwnn *wwnn,
+			      unsigned char macaddr[])
+{
+	int count = 0, all = 0, delthisone;
+	struct virtpci_dev *tmpvpcidev, *prevvpcidev, *dellist = NULL;
+	unsigned long flags;
+
+#define DEL_CONTINUE { \
+	prevvpcidev = tmpvpcidev;\
+	tmpvpcidev = tmpvpcidev->next;\
+	continue; \
+}
+
+	if ((devtype != VIRTHBA_TYPE) && (devtype != VIRTNIC_TYPE)) {
+		LOGERR("**** FAILED to delete device; devtype:%d not vhba:%d or vnic:%d\n",
+		       devtype, VIRTHBA_TYPE, VIRTNIC_TYPE);
+		return 0;
+	}
+
+	/* see if we are to delete all - NOTE: all implies we have a
+	 * valid parentbus
+	 */
+	all = ((devtype == VIRTHBA_TYPE) && (wwnn == NULL)) ||
+	    ((devtype == VIRTNIC_TYPE) && (macaddr == NULL));
+
+	/* find all the vhba or vnic or both in virtpci device list
+	* keep list of ones we are deleting so we can call
+	* device_unregister after we release the lock; otherwise we
+	* encounter "schedule while atomic"
+	*/
+	write_lock_irqsave(&VpcidevListLock, flags);
+	for (tmpvpcidev = VpcidevListHead, prevvpcidev = NULL; tmpvpcidev;) {
+		if (tmpvpcidev->devtype != devtype)
+			DEL_CONTINUE;
+
+		if (all) {
+			delthisone =
+			    (tmpvpcidev->generic_dev.parent == parentbus);
+			/* we're deleting all vhbas or vnics on the
+			 * specified parent bus
+			 */
+		} else if (devtype == VIRTHBA_TYPE) {
+			delthisone =
+			    ((tmpvpcidev->scsi.wwnn.wwnn1 == wwnn->wwnn1) &&
+			     (tmpvpcidev->scsi.wwnn.wwnn2 == wwnn->wwnn2));
+			/* devtype is vhba, we're deleting vhba whose
+			 * wwnn matches the current device's wwnn
+			 */
+		} else {	/* VIRTNIC_TYPE */
+			delthisone =
+			    memcmp(tmpvpcidev->net.mac_addr, macaddr,
+				   MAX_MACADDR_LEN) == 0;
+			/* devtype is vnic, we're deleting vnic whose
+			* macaddr matches the current device's macaddr
+			*/
+		}
+
+		if (!delthisone)
+			DEL_CONTINUE;
+
+		/* take vhba/vnic out of the list */
+		if (prevvpcidev)
+			/* not at head */
+			prevvpcidev->next = tmpvpcidev->next;
+		else
+			VpcidevListHead = tmpvpcidev->next;
+
+		/* add it to our deletelist */
+		tmpvpcidev->next = dellist;
+		dellist = tmpvpcidev;
+
+		count++;
+		if (!all)
+			break;	/* done */
+		/* going to top of loop again - set tmpvpcidev to next
+		 * one we're to process
+		 */
+		if (prevvpcidev)
+			tmpvpcidev = prevvpcidev->next;
+		else
+			tmpvpcidev = VpcidevListHead;
+	}
+	write_unlock_irqrestore(&VpcidevListLock, flags);
+
+	if (!all && (count == 0)) {
+		LOGERR("**** FAILED to find vhba/vnic in the list\n");
+		return 0;
+	}
+
+	/* now delete each one from delete list */
+	while (dellist) {
+		/* save next */
+		tmpvpcidev = dellist->next;
+		/* delete the vhba/vnic at dellist */
+		DELETE_ONE_VPCIDEV(dellist);
+		/* do next */
+		dellist = tmpvpcidev;
+	}
+
+	return count;
+}
+
+static void virtpci_device_release(struct device *dev_)
+{
+	/* this function is called when the last reference to the
+	 * device is removed
+	 */
+	LOGINF("In virtpci_device_release:%p - NOT YET IMPLEMENTED\n", dev_);
+}
+
+/*****************************************************/
+/* Driver functions                                  */
+/*****************************************************/
+
+#define kobj_to_device_driver(obj) container_of(obj, struct device_driver, kobj)
+#define attribute_to_driver_attribute(obj) \
+	container_of(obj, struct driver_attribute, attr)
+
+static ssize_t virtpci_driver_attr_show(struct kobject *kobj,
+					struct attribute *attr,
+					char *buf)
+{
+	struct driver_attribute *dattr = attribute_to_driver_attribute(attr);
+	ssize_t ret = 0;
+
+	struct driver_private *dprivate = to_driver(kobj);
+	struct device_driver *driver;
+	if (dprivate != NULL)
+		driver = dprivate->driver;
+	else
+		driver = NULL;
+
+	DBGINF("In virtpci_driver_attr_show driver->name:%s\n", driver->name);
+	if (driver) {
+		if (dattr->show)
+			ret = dattr->show(driver, buf);
+	}
+	return ret;
+}
+
+static ssize_t virtpci_driver_attr_store(struct kobject *kobj,
+					 struct attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct driver_attribute *dattr = attribute_to_driver_attribute(attr);
+	ssize_t ret = 0;
+
+	struct driver_private *dprivate = to_driver(kobj);
+	struct device_driver *driver;
+	if (dprivate != NULL)
+		driver = dprivate->driver;
+	else
+		driver = NULL;
+
+	DBGINF("In virtpci_driver_attr_store driver->name:%s\n", driver->name);
+
+	if (driver) {
+		if (dattr->store)
+			ret = dattr->store(driver, buf, count);
+	}
+	return ret;
+}
+
+/* register a new virtpci driver */
+int virtpci_register_driver(struct virtpci_driver *drv)
+{
+	int result = 0;
+
+	DBGINF("In virtpci_register_driver\n");
+
+	if (drv->id_table == NULL) {
+		LOGERR("id_table missing\n");
+		return 1;
+	}
+	/* initialize core driver fields needed to call driver_register */
+	drv->core_driver.name = drv->name;	/* name of driver in sysfs */
+	drv->core_driver.bus = &virtpci_bus_type;	/* type of bus this
+							 * driver works with */
+	drv->core_driver.probe = virtpci_device_probe;	/* called to query the
+							 * existence of a
+							 * specific device and
+							 * whether this driver
+							 *can work with it */
+	drv->core_driver.remove = virtpci_device_remove; /* called when the
+							  * device is removed
+							  * from the system */
+	/* register with core */
+	result = driver_register(&drv->core_driver);
+	/* calls bus_add_driver which calls driver_attach and
+	 * module_add_driver
+	 */
+	if (result)
+		return result;	/* failed */
+
+	drv->core_driver.p->kobj.ktype = &virtpci_driver_kobj_type;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(virtpci_register_driver);
+
+void virtpci_unregister_driver(struct virtpci_driver *drv)
+{
+	DBGINF("In virtpci_unregister_driver drv:%p\n", drv);
+	driver_unregister(&drv->core_driver);
+	/* driver_unregister calls bus_remove_driver
+	 * bus_remove_driver calls device_detach
+	 * device_detach calls device_release_driver for each of the
+	 * driver's devices
+	 * device_release driver calls drv->remove which is
+	 * virtpci_device_remove
+	 * virtpci_device_remove calls virthba_remove
+	 */
+	DBGINF("Leaving\n");
+}
+EXPORT_SYMBOL_GPL(virtpci_unregister_driver);
+
+/*****************************************************/
+/* proc filesystem functions						 */
+/*****************************************************/
+struct print_vbus_info {
+	int *length;
+	char *buf;
+};
+
+static int print_vbus(struct device *vbus, void *data)
+{
+	struct print_vbus_info *p = (struct print_vbus_info *) data;
+	int l = *(p->length);
+
+	*(p->length) = l + sprintf(p->buf + l, "bus_id:%s\n", dev_name(vbus));
+	return 0;		/* no error */
+}
+
+static ssize_t info_proc_read(struct file *file, char __user *buf,
+			      size_t len, loff_t *offset)
+{
+	int length = 0;
+	struct virtpci_dev *tmpvpcidev;
+	unsigned long flags;
+	struct print_vbus_info printparam;
+	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 + length, "CHANSOCK is not defined.\n");
+
+	length += sprintf(vbuf + length, "\n Virtual PCI Bus devices\n");
+	printparam.length = &length;
+	printparam.buf = vbuf;
+	if (bus_for_each_dev(&virtpci_bus_type, NULL,
+			     (void *) &printparam, print_vbus))
+		LOGERR("delete of all vbus failed\n");
+
+	length += sprintf(vbuf + length, "\n Virtual PCI devices\n");
+	read_lock_irqsave(&VpcidevListLock, flags);
+	tmpvpcidev = VpcidevListHead;
+	while (tmpvpcidev) {
+		if (tmpvpcidev->devtype == VIRTHBA_TYPE) {
+			length += sprintf(vbuf + length, "[%d:%d] VHba:%08x:%08x max-config:%d-%d-%d-%d",
+				    tmpvpcidev->busNo, tmpvpcidev->deviceNo,
+				    tmpvpcidev->scsi.wwnn.wwnn1,
+				    tmpvpcidev->scsi.wwnn.wwnn2,
+				    tmpvpcidev->scsi.max.max_channel,
+				    tmpvpcidev->scsi.max.max_id,
+				    tmpvpcidev->scsi.max.max_lun,
+				    tmpvpcidev->scsi.max.cmd_per_lun);
+		} else {
+			length += sprintf(vbuf + length, "[%d:%d] VNic:%02x:%02x:%02x:%02x:%02x:%02x num_rcv_bufs:%d mtu:%d",
+				    tmpvpcidev->busNo, tmpvpcidev->deviceNo,
+				    tmpvpcidev->net.mac_addr[0],
+				    tmpvpcidev->net.mac_addr[1],
+				    tmpvpcidev->net.mac_addr[2],
+				    tmpvpcidev->net.mac_addr[3],
+				    tmpvpcidev->net.mac_addr[4],
+				    tmpvpcidev->net.mac_addr[5],
+				    tmpvpcidev->net.num_rcv_bufs,
+				    tmpvpcidev->net.mtu);
+		}
+		length +=
+		    sprintf(vbuf + length, " chanptr:%p\n",
+			    tmpvpcidev->queueinfo.chan);
+		tmpvpcidev = tmpvpcidev->next;
+	}
+	read_unlock_irqrestore(&VpcidevListLock, flags);
+
+	length +=
+	    sprintf(vbuf + length, "\nModule build: Date:%s Time:%s\n", __DATE__,
+		    __TIME__);
+
+	length += sprintf(vbuf + length, "\n");
+	if (copy_to_user(buf, vbuf, length)) {
+		kfree(vbuf);
+		return -EFAULT;
+	}
+
+	kfree(vbuf);
+	*offset += length;
+	return length;
+}
+
+static ssize_t virt_proc_write(struct file *file, const char __user *buffer,
+			       size_t count, loff_t *ppos)
+{
+	char buf[count];
+	int type, i, action = 0xffff;
+	unsigned int busno, deviceno;
+	void *chanptr;
+	struct add_vbus_guestpart busaddparams;
+	struct add_virt_guestpart addparams;
+	struct del_vbus_guestpart busdelparams;
+	struct del_virt_guestpart delparams;
+	GUID dummyGuid = GUID0;
+#ifdef STORAGE_CHANNEL
+	U64 storagechannel;
+#endif
+
+#define PRINT_USAGE_RETURN {\
+	LOGERR("usage: 0-0-<chanptr>					==> delete vhba\n"); \
+	LOGERR("usage: 0-1-<chanptr>-<busNo>-<deviceNo>	==> add vhba\n"); \
+	LOGERR("usage: 0-f-<busNo>						==> delete all vhbas\n"); \
+	LOGERR("\n"); \
+	LOGERR("usage: 1-0-<chanptr>					==> delete vnic\n"); \
+	LOGERR("usage: 1-1-<chanptr>-<busNo>-<deviceNo>	==> add vnic\n"); \
+	LOGERR("usage: 1-f-<busNo>						==> delete all vnics\n"); \
+	LOGERR("\n"); \
+	LOGERR("usage: 6-0-<busNo>						==> delete vbus\n"); \
+	LOGERR("usage: 6-1-<busNo>						==> add vbus\n"); \
+	LOGERR("usage: 6-f								==> delete all vbuses\n"); \
+	LOGERR("usage: 98-<busNo>-<deviceNo>			==> INJECT Client delete vnic\n"); \
+	LOGERR("usage: 99-<chanptr>-<busNo>-<deviceNo>	==> INJECT Client add vnic\n"); \
+	return -EINVAL; \
+}
+
+	if (copy_from_user(buf, buffer, count)) {
+		LOGERR("copy_from_user failed.\n");
+		return -EFAULT;
+	}
+
+	i = sscanf(buf, "%x-%x", &type, &action);
+	if (i < 2)
+		PRINT_USAGE_RETURN;
+
+	if (type == 0x98) {
+		/* client inject delete vnic */
+		i = sscanf(buf, "%x-%d-%d", &type, &busno, &deviceno);
+		if (i != 3)
+			PRINT_USAGE_RETURN;
+		uislib_client_inject_del_vnic(busno, deviceno);
+		return count;	/* success */
+	} else if (type == 0x99) {
+		/* client inject add vnic */
+		i = sscanf(buf, "%x-%p-%d-%d", &type, &chanptr, &busno,
+			   &deviceno);
+		if (i != 4)
+			PRINT_USAGE_RETURN;
+		if (!uislib_client_inject_add_vnic(busno, deviceno,
+						   __pa(chanptr),
+						   MIN_IO_CHANNEL_SIZE,
+						   1, /* test msg */
+						   dummyGuid, /* inst guid */
+						   NULL)) { /*interrupt info */
+			LOGERR("FAILED to inject add vnic\n");
+			return -EFAULT;
+		}
+		return count;	/* success */
+	}
+
+	if ((type != VIRTHBA_TYPE) && (type != VIRTNIC_TYPE)
+	    && (type != VIRTBUS_TYPE))
+		PRINT_USAGE_RETURN;
+
+	if (type == VIRTBUS_TYPE) {
+		i = sscanf(buf, "%x-%x-%d", &type, &action, &busno);
+		switch (action) {
+		case 0:
+			/* delete vbus */
+			if (i != 3)
+				break;
+			busdelparams.busNo = busno;
+			if (delete_vbus(&busdelparams))
+				return count;	/* success */
+			return -EFAULT;
+
+		case 1:
+			/* add vbus */
+			if (i != 3)
+				break;
+			busaddparams.chanptr = NULL;	/* NOT YET USED */
+			busaddparams.busNo = busno;
+			if (add_vbus(&busaddparams))
+				return count;	/* success */
+			return -EFAULT;
+
+		case 0xf:
+			/* delete all vbuses and all vhbas/vnics on the buses */
+			if (i != 2)
+				break;
+			delete_all();
+			return count;	/* success */
+		default:
+			break;
+		}
+		PRINT_USAGE_RETURN;
+	}
+
+	/* if (type == VIRTNIC_TYPE) or         if (type == VIRTHBA_TYPE) */
+	switch (action) {
+	case 0:
+		/* delete vhba/vnic */
+		i = sscanf(buf, "%x-%x-%p", &type, &action, &chanptr);
+		if (i != 3)
+			break;
+		delparams.chanptr = chanptr;
+		if (type == VIRTHBA_TYPE) {
+			if (delete_vhba(&delparams))
+				return count;	/* success */
+		} else {
+			if (delete_vnic(&delparams))
+				return count;	/* success */
+		}
+		return -EFAULT;
+
+	case 1:
+		/* add vhba/vnic */
+		i = sscanf(buf, "%x-%x-%p-%d-%d", &type, &action, &chanptr,
+			   &busno, &deviceno);
+		if (i != 5)
+			break;
+		addparams.chanptr = chanptr;
+		addparams.busNo = busno;
+		addparams.deviceNo = deviceno;
+		if (type == VIRTHBA_TYPE) {
+			if (add_vhba(&addparams))
+				return count;	/* success */
+		} else {
+			if (add_vnic(&addparams))
+				return count;	/* success */
+		}
+		return -EFAULT;
+
+#ifdef STORAGE_CHANNEL
+	case 2:
+		/* add vhba */
+		i = sscanf(buf, "%x-%x-%d-%d", &type, &action, &busno,
+			   &deviceno);
+		if (i != 4)
+			break;
+		storagechannel = uislib_storage_channel(0);	/* Get my storage channel */
+		/* ioremap_cache it now */
+		addparams.chanptr =
+		    (void *) ioremap_cache(storagechannel, IO_CHANNEL_SIZE);
+		if (addparams.chanptr == NULL) {
+			LOGERR("Failure to get remap storage channel.\n");
+			return -EFAULT;
+		}
+		addparams.busNo = busno;
+		addparams.deviceNo = deviceno;
+		if (type == VIRTHBA_TYPE) {
+			if (add_vhba(&addparams))
+				return count;	/* success */
+		}
+		return -EFAULT;
+#endif
+	case 0xf:
+		/* delete all vhbas/vnics */
+		i = sscanf(buf, "%x-%x-%d", &type, &action, &busno);
+		if (i != 3)
+			break;
+		busdelparams.busNo = busno;
+		delete_all_virt(type, &busdelparams);
+		return count;	/* success */
+	default:
+		break;
+	}
+	PRINT_USAGE_RETURN;
+}
+
+/*****************************************************/
+/* Module Init & Exit functions                      */
+/*****************************************************/
+
+static int __init virtpci_mod_init(void)
+{
+	int ret;
+
+
+	LOGINF("Module build: Date:%s Time:%s...\n", __DATE__, __TIME__);
+
+	POSTCODE_LINUX_2(VPCI_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+
+	ret = bus_register(&virtpci_bus_type);
+	/* creates /sys/bus/uisvirtpci which contains devices &
+	 * drivers directory
+	 */
+	if (ret) {
+		LOGERR("bus_register ****FAILED:%d\n", ret);
+		POSTCODE_LINUX_3(VPCI_CREATE_FAILURE_PC, ret,
+				 POSTCODE_SEVERITY_ERR);
+		return ret;
+	}
+	DBGINF("bus_register successful\n");
+	BusDeviceInfo_Init(&Bus_DriverInfo,
+			   "clientbus", "virtpci",
+			   VERSION, NULL, __DATE__, __TIME__);
+
+	/* create a root bus used to parent all the virtpci buses. */
+	ret = device_register(&virtpci_rootbus_device);
+	if (ret) {
+		LOGERR("device_register FAILED:%d\n", ret);
+		bus_unregister(&virtpci_bus_type);
+		POSTCODE_LINUX_3(VPCI_CREATE_FAILURE_PC, ret,
+				 POSTCODE_SEVERITY_ERR);
+		return ret;
+	}
+	DBGINF("device_register successful ret:%x\n", ret);
+
+	if (!uisctrl_register_req_handler(2, (void *) &virtpci_ctrlchan_func,
+					  &Chipset_DriverInfo)) {
+		LOGERR("uisctrl_register_req_handler ****FAILED.\n");
+		POSTCODE_LINUX_2(VPCI_CREATE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+		device_unregister(&virtpci_rootbus_device);
+		bus_unregister(&virtpci_bus_type);
+		return -1;
+	}
+
+	LOGINF("successfully registered virtpci_ctrlchan_func (0x%p) as callback.\n",
+	     (void *) &virtpci_ctrlchan_func);
+	/* create the proc directories */
+	virtpci_proc_dir = proc_mkdir(DIR_PROC_ENTRY, NULL);
+	virt_proc_entry = proc_create(VIRT_PROC_ENTRY_FN, 0, virtpci_proc_dir,
+				      &proc_virt_fops);
+	info_proc_entry = proc_create(INFO_PROC_ENTRY_FN, 0, virtpci_proc_dir,
+				      &proc_info_fops);
+	LOGINF("Leaving\n");
+	POSTCODE_LINUX_2(VPCI_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO);
+	return 0;
+}
+
+static void __exit virtpci_mod_exit(void)
+{
+	LOGINF("virtpci_mod_exit...\n");
+
+	/* unregister the callback function */
+	if (!uisctrl_register_req_handler(2, NULL, NULL))
+		LOGERR("uisctrl_register_req_handler ****FAILED.\n");
+
+	device_unregister(&virtpci_rootbus_device);
+	bus_unregister(&virtpci_bus_type);
+
+	if (virt_proc_entry)
+		remove_proc_entry(VIRT_PROC_ENTRY_FN, virtpci_proc_dir);
+
+	if (info_proc_entry)
+		remove_proc_entry(INFO_PROC_ENTRY_FN, virtpci_proc_dir);
+
+	if (virtpci_proc_dir)
+		remove_proc_entry(DIR_PROC_ENTRY, NULL);
+
+	LOGINF("Leaving\n");
+
+}
+
+module_init(virtpci_mod_init);
+module_exit(virtpci_mod_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Usha Srinivasan");
+MODULE_ALIAS("uisvirtpci");
+
Index: upstream-staging/drivers/staging/unisys/virtpci/virtpci.h
===================================================================
--- /dev/null
+++ upstream-staging/drivers/staging/unisys/virtpci/virtpci.h
@@ -0,0 +1,104 @@
+/* virtpci.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.
+ */
+
+/*
+ * Unisys Virtual PCI driver header
+ */
+
+#ifndef __VIRTPCI_H__
+#define __VIRTPCI_H__
+
+#include "uisqueue.h"
+#include <linux/version.h>
+
+#define PCI_DEVICE_ID_VIRTHBA 0xAA00
+#define PCI_DEVICE_ID_VIRTNIC 0xAB00
+
+struct scsi_adap_info {
+	void *scsihost;		/* scsi host if this device is a scsi hba */
+	struct vhba_wwnn wwnn;	/* the world wide node name of vhba */
+	struct vhba_config_max max;	/* various max specifications used
+					 * to config vhba */
+};
+
+struct net_adap_info {
+	struct net_device *netdev;	/* network device if this
+					 * device is a NIC */
+	u8 mac_addr[MAX_MACADDR_LEN];
+	int num_rcv_bufs;
+	unsigned mtu;
+	GUID zoneGuid;
+};
+
+typedef enum {
+	VIRTHBA_TYPE = 0,
+	VIRTNIC_TYPE = 1,
+	VIRTBUS_TYPE = 6,
+} VIRTPCI_DEV_TYPE;
+
+struct virtpci_dev {
+	VIRTPCI_DEV_TYPE devtype;	/* indicates type of the
+					 * virtual pci device */
+	struct virtpci_driver *mydriver;	/* which driver has allocated
+						 * this device */
+	unsigned short vendor;	/* vendor id for device */
+	unsigned short device;	/* device id for device */
+	U32 busNo;		/* number of bus on which device exists */
+	U32 deviceNo;		/* device's number on the bus */
+	struct InterruptInfo intr;	/* interrupt info */
+	struct device generic_dev;	/* generic device */
+	union {
+		struct scsi_adap_info scsi;
+		struct net_adap_info net;
+	};
+
+	struct uisqueue_info queueinfo;	/* holds ptr to channel where cmds &
+					 * rsps are queued & retrieved */
+	struct virtpci_dev *next;	/* points to next virtpci device */
+};
+
+struct virtpci_driver {
+	struct list_head node;
+	const char *name;	/* the name of the driver in sysfs */
+	const char *version;
+	const char *vertag;
+	const char *build_date;
+	const char *build_time;
+	const struct pci_device_id *id_table;	/* must be non-NULL for probe
+						 * to be called */
+	int (*probe)(struct virtpci_dev *dev,
+		      const struct pci_device_id *id); /* device inserted */
+	void (*remove)(struct virtpci_dev *dev); /* Device removed (NULL if
+						    * not a hot-plug capable
+						    * driver) */
+	int (*suspend)(struct virtpci_dev *dev,
+			u32 state);		   /* Device suspended */
+	int (*resume)(struct virtpci_dev *dev);	/* Device woken up */
+	int (*enable_wake)(struct virtpci_dev *dev,
+			    u32 state, int enable);	/* Enable wake event */
+	struct device_driver core_driver;	/* VIRTPCI core fills this in */
+};
+
+#define	driver_to_virtpci_driver(in_drv) \
+	container_of(in_drv, struct virtpci_driver, core_driver)
+#define device_to_virtpci_dev(in_dev) \
+	container_of(in_dev, struct virtpci_dev, generic_dev)
+
+int virtpci_register_driver(struct virtpci_driver *);
+void virtpci_unregister_driver(struct virtpci_driver *);
+
+#endif /* __VIRTPCI_H__ */

_______________________________________________
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