staging: virtpci driver
authorKen Cox <jkc@redhat.com>
Tue, 4 Mar 2014 13:58:10 +0000 (07:58 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Mar 2014 01:03:21 +0000 (17:03 -0800)
The virtpci module handles the bus functions for virthba, and virtnic.

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

index 0b1e0c5..52e3725 100644 (file)
@@ -14,5 +14,6 @@ source "drivers/staging/unisys/visorchannel/Kconfig"
 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 f9174f5..56ee16c 100644 (file)
@@ -6,3 +6,4 @@ obj-$(CONFIG_UNISYS_VISORCHANNEL)       += visorchannel/
 obj-$(CONFIG_UNISYS_VISORCHIPSET)      += visorchipset/
 obj-$(CONFIG_UNISYS_CHANNELSTUB)       += channels/
 obj-$(CONFIG_UNISYS_UISLIB)            += uislib/
+obj-$(CONFIG_UNISYS_VIRTPCI)           += virtpci/
diff --git a/drivers/staging/unisys/virtpci/Kconfig b/drivers/staging/unisys/virtpci/Kconfig
new file mode 100644 (file)
index 0000000..e59efcb
--- /dev/null
@@ -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.
+
diff --git a/drivers/staging/unisys/virtpci/Makefile b/drivers/staging/unisys/virtpci/Makefile
new file mode 100644 (file)
index 0000000..f9399aa
--- /dev/null
@@ -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
+
diff --git a/drivers/staging/unisys/virtpci/virtpci.c b/drivers/staging/unisys/virtpci/virtpci.c
new file mode 100644 (file)
index 0000000..5700e4b
--- /dev/null
@@ -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");
+
diff --git a/drivers/staging/unisys/virtpci/virtpci.h b/drivers/staging/unisys/virtpci/virtpci.h
new file mode 100644 (file)
index 0000000..b8fd07b
--- /dev/null
@@ -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__ */