From 3703987cd427ca4ca1c7e98308be7f3036007a37 Mon Sep 17 00:00:00 2001 From: Erik Arfvidson Date: Tue, 5 May 2015 18:36:00 -0400 Subject: [PATCH] staging: unisys: add visorbus driver This base driver provides bus functionality to visorhid, visorhba, and visornic which will be later added to our driver base. Visorbus supports sPar bus model and manages bus specific functionality. It maintains the sysfs subtree /sys/devices/visorbus*/.It is responsible for device creation and destruction of the devices on its bus. Signed-off-by: Erik Arfvidson Signed-off-by: Benjamin Romer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/unisys/Kconfig | 1 + drivers/staging/unisys/Makefile | 1 + .../include/channels/vbuschannel.h | 2 +- drivers/staging/unisys/visorbus/Kconfig | 10 + drivers/staging/unisys/visorbus/Makefile | 14 + .../staging/unisys/visorbus/businst_attr.c | 103 ++ .../staging/unisys/visorbus/businst_attr.h | 40 + .../staging/unisys/visorbus/channel_attr.c | 227 +++ .../staging/unisys/visorbus/channel_attr.h | 27 + .../unisys/visorbus/devmajorminor_attr.c | 192 ++ .../unisys/visorbus/devmajorminor_attr.h | 31 + drivers/staging/unisys/visorbus/visorbus.h | 166 ++ .../staging/unisys/visorbus/visorbus_main.c | 1643 +++++++++++++++++ .../unisys/visorbus/visorbus_private.h | 50 + 14 files changed, 2506 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/unisys/visorbus/Kconfig create mode 100644 drivers/staging/unisys/visorbus/Makefile create mode 100644 drivers/staging/unisys/visorbus/businst_attr.c create mode 100644 drivers/staging/unisys/visorbus/businst_attr.h create mode 100644 drivers/staging/unisys/visorbus/channel_attr.c create mode 100644 drivers/staging/unisys/visorbus/channel_attr.h create mode 100644 drivers/staging/unisys/visorbus/devmajorminor_attr.c create mode 100644 drivers/staging/unisys/visorbus/devmajorminor_attr.h create mode 100644 drivers/staging/unisys/visorbus/visorbus.h create mode 100644 drivers/staging/unisys/visorbus/visorbus_main.c create mode 100644 drivers/staging/unisys/visorbus/visorbus_private.h diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig index 14e1ea6803f8..8d056b55ced7 100644 --- a/drivers/staging/unisys/Kconfig +++ b/drivers/staging/unisys/Kconfig @@ -12,5 +12,6 @@ if UNISYSSPAR source "drivers/staging/unisys/visorutil/Kconfig" source "drivers/staging/unisys/visorchannel/Kconfig" source "drivers/staging/unisys/visorchipset/Kconfig" +source "drivers/staging/unisys/visorbus/Kconfig" endif # UNISYSSPAR diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile index 97e750de4bef..1ed9d39ff230 100644 --- a/drivers/staging/unisys/Makefile +++ b/drivers/staging/unisys/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil/ obj-$(CONFIG_UNISYS_VISORCHANNEL) += visorchannel/ obj-$(CONFIG_UNISYS_VISORCHIPSET) += visorchipset/ +obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ diff --git a/drivers/staging/unisys/common-spar/include/channels/vbuschannel.h b/drivers/staging/unisys/common-spar/include/channels/vbuschannel.h index 2c42ce16e0cf..5ed83a3f1428 100644 --- a/drivers/staging/unisys/common-spar/include/channels/vbuschannel.h +++ b/drivers/staging/unisys/common-spar/include/channels/vbuschannel.h @@ -54,7 +54,7 @@ static const uuid_le spar_vbus_channel_protocol_uuid = #define SPAR_VBUS_CHANNEL_OK_SERVER(actual_bytes) \ (spar_check_channel_server(spar_vbus_channel_protocol_uuid, \ "vbus", \ - sizeof(struct ultra_vbus_channel_protocol),\ + sizeof(struct spar_vbus_channel_protocol),\ actual_bytes)) #pragma pack(push, 1) /* both GCC and VC now allow this pragma */ diff --git a/drivers/staging/unisys/visorbus/Kconfig b/drivers/staging/unisys/visorbus/Kconfig new file mode 100644 index 000000000000..0141528657e2 --- /dev/null +++ b/drivers/staging/unisys/visorbus/Kconfig @@ -0,0 +1,10 @@ +# +# Unisys visorbus configuration +# + +config UNISYS_VISORBUS + tristate "Unisys visorbus driver" + depends on UNISYSSPAR && UNISYS_VISORUTIL && UNISYS_VISORCHANNEL && UNISYS_VISORCHIPSET + ---help--- + If you say Y here, you will enable the Unisys visorbus driver. + diff --git a/drivers/staging/unisys/visorbus/Makefile b/drivers/staging/unisys/visorbus/Makefile new file mode 100644 index 000000000000..60bb96bad239 --- /dev/null +++ b/drivers/staging/unisys/visorbus/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for Unisys visorbus +# + +obj-$(CONFIG_UNISYS_VISORBUS) += visorbus.o + +visorbus-y := visorbus_main.o devmajorminor_attr.o businst_attr.o channel_attr.o + +ccflags-y += -Idrivers/staging/unisys/include +ccflags-y += -Idrivers/staging/unisys/visorchannel +ccflags-y += -Idrivers/staging/unisys/visorchipset +ccflags-y += -Idrivers/staging/unisys/common-spar/include +ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels +ccflags-y += -Idrivers/staging/unisys/visorutil diff --git a/drivers/staging/unisys/visorbus/businst_attr.c b/drivers/staging/unisys/visorbus/businst_attr.c new file mode 100644 index 000000000000..b3fea9d93d5e --- /dev/null +++ b/drivers/staging/unisys/visorbus/businst_attr.c @@ -0,0 +1,103 @@ +/* businst_attr.c + * + * Copyright (C) 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. + */ + +/* This is actually something they forgot to put in the kernel. + * struct bus_type in the kernel SHOULD have a "busses" member, which + * should be treated similarly to the "devices" and "drivers" members. + * There SHOULD be: + * - a "businst_attribute" analogous to the existing "bus_attribute" + * - a "businst_create_file" and "businst_remove_file" analogous to the + * existing "bus_create_file" and "bus_remove_file". + * That's what I created businst.c and businst.h to do. + * + * We want to add the "busses" sub-tree in sysfs, where we will house the + * names and properties of each bus instance: + * + * /sys/bus// + * version + * devices + * --> /sys/devices/ + * --> /sys/devices/ + * drivers + * + * + * + * ... + * + * + * + * ... + * >> busses + * >> + * >> + * >> + * >> ... + * >> + * >> + * >> + * >> ... + * + * I considered adding bus instance properties under + * /sys/devices/. But I thought there may be existing + * notions that ONLY device sub-trees should live under + * /sys/devices/. So I stayed out of there. + * + */ + +#include "businst_attr.h" + +#define to_businst_attr(_attr) \ + container_of(_attr, struct businst_attribute, attr) +#define to_visorbus_devdata(obj) \ + container_of(obj, struct visorbus_devdata, kobj) +#define CURRENT_FILE_PC VISOR_BUS_PC_businst_attr_c + +ssize_t businst_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct businst_attribute *businst_attr = to_businst_attr(attr); + struct visorbus_devdata *bus = to_visorbus_devdata(kobj); + ssize_t ret = 0; + + if (businst_attr->show) + ret = businst_attr->show(bus, buf); + return ret; +} + +ssize_t businst_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct businst_attribute *businst_attr = to_businst_attr(attr); + struct visorbus_devdata *bus = to_visorbus_devdata(kobj); + ssize_t ret = 0; + + if (businst_attr->store) + ret = businst_attr->store(bus, buf, count); + return ret; +} + +int businst_create_file(struct visorbus_devdata *bus, + struct businst_attribute *attr) +{ + return sysfs_create_file(&bus->kobj, &attr->attr); +} + +void businst_remove_file(struct visorbus_devdata *bus, + struct businst_attribute *attr) +{ + sysfs_remove_file(&bus->kobj, &attr->attr); +} diff --git a/drivers/staging/unisys/visorbus/businst_attr.h b/drivers/staging/unisys/visorbus/businst_attr.h new file mode 100644 index 000000000000..e9fb36a692cb --- /dev/null +++ b/drivers/staging/unisys/visorbus/businst_attr.h @@ -0,0 +1,40 @@ +/* businst_attr.h + * + * Copyright (C) 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __BUSINST_H__ +#define __BUSINST_H__ + +#include "visorbus_private.h" /* just to get visorbus_devdata declaration */ +#include "timskmod.h" + +struct businst_attribute { + struct attribute attr; + ssize_t (*show)(struct visorbus_devdata*, char *buf); + ssize_t (*store)(struct visorbus_devdata*, const char *buf, + size_t count); +}; + +ssize_t businst_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf); +ssize_t businst_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count); +int businst_create_file(struct visorbus_devdata *bus, + struct businst_attribute *attr); +void businst_remove_file(struct visorbus_devdata *bus, + struct businst_attribute *attr); + +#endif diff --git a/drivers/staging/unisys/visorbus/channel_attr.c b/drivers/staging/unisys/visorbus/channel_attr.c new file mode 100644 index 000000000000..0d7184ac1cbe --- /dev/null +++ b/drivers/staging/unisys/visorbus/channel_attr.c @@ -0,0 +1,227 @@ +/* channel_attr.c + * + * Copyright (C) 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. + */ + +/* Implement publishing of channel attributes under: + * + * /sys/bus/visorbus/dev/channel + * + */ + +#include "channel_attr.h" +#define CURRENT_FILE_PC VISOR_BUS_PC_channel_attr_c +#define to_channel_attr(_attr) \ + container_of(_attr, struct channel_attribute, attr) +#define to_visor_device_from_kobjchannel(obj) \ + container_of(obj, struct visor_device, kobjchannel) + +struct channel_attribute { + struct attribute attr; + ssize_t (*show)(struct visor_device*, char *buf); + ssize_t (*store)(struct visor_device*, const char *buf, size_t count); +}; + +/* begin implementation of specific channel attributes to appear under +* /sys/bus/visorbus/dev/channel +*/ +static ssize_t devicechannel_attr_physaddr(struct visor_device *dev, char *buf) +{ + if (!dev->visorchannel) + return 0; + return snprintf(buf, PAGE_SIZE, "0x%Lx\n", + visorchannel_get_physaddr(dev->visorchannel)); +} + +static ssize_t devicechannel_attr_nbytes(struct visor_device *dev, char *buf) +{ + if (!dev->visorchannel) + return 0; + return snprintf(buf, PAGE_SIZE, "0x%lx\n", + visorchannel_get_nbytes(dev->visorchannel)); +} + +static ssize_t devicechannel_attr_clientpartition(struct visor_device *dev, + char *buf) { + if (!dev->visorchannel) + return 0; + return snprintf(buf, PAGE_SIZE, "0x%Lx\n", + visorchannel_get_clientpartition(dev->visorchannel)); +} + +static ssize_t devicechannel_attr_typeguid(struct visor_device *dev, char *buf) +{ + char s[99]; + + if (!dev->visorchannel) + return 0; + return snprintf(buf, PAGE_SIZE, "%s\n", + visorchannel_id(dev->visorchannel, s)); +} + +static ssize_t devicechannel_attr_zoneguid(struct visor_device *dev, char *buf) +{ + char s[99]; + + if (!dev->visorchannel) + return 0; + return snprintf(buf, PAGE_SIZE, "%s\n", + visorchannel_zoneid(dev->visorchannel, s)); +} + +static ssize_t devicechannel_attr_typename(struct visor_device *dev, char *buf) +{ + int i = 0; + struct bus_type *xbus = dev->device.bus; + struct device_driver *xdrv = dev->device.driver; + struct visor_driver *drv = NULL; + + if (!dev->visorchannel || !xbus || !xdrv) + return 0; + i = xbus->match(&dev->device, xdrv); + if (!i) + return 0; + drv = to_visor_driver(xdrv); + return snprintf(buf, PAGE_SIZE, "%s\n", drv->channel_types[i - 1].name); +} + +static ssize_t devicechannel_attr_dump(struct visor_device *dev, char *buf) +{ + int count = 0; +/* TODO: replace this with debugfs code + struct seq_file *m = NULL; + if (dev->visorchannel == NULL) + return 0; + m = visor_seq_file_new_buffer(buf, PAGE_SIZE - 1); + if (m == NULL) + return 0; + visorchannel_debug(dev->visorchannel, 1, m, 0); + count = m->count; + visor_seq_file_done_buffer(m); + m = NULL; +*/ + return count; +} + +static struct channel_attribute all_channel_attrs[] = { + __ATTR(physaddr, S_IRUGO, + devicechannel_attr_physaddr, NULL), + __ATTR(nbytes, S_IRUGO, + devicechannel_attr_nbytes, NULL), + __ATTR(clientpartition, S_IRUGO, + devicechannel_attr_clientpartition, NULL), + __ATTR(typeguid, S_IRUGO, + devicechannel_attr_typeguid, NULL), + __ATTR(zoneguid, S_IRUGO, + devicechannel_attr_zoneguid, NULL), + __ATTR(typename, S_IRUGO, + devicechannel_attr_typename, NULL), + __ATTR(dump, S_IRUGO, + devicechannel_attr_dump, NULL), +}; + +/* end implementation of specific channel attributes */ + +static ssize_t channel_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct channel_attribute *channel_attr = to_channel_attr(attr); + struct visor_device *dev = to_visor_device_from_kobjchannel(kobj); + ssize_t ret = 0; + + if (channel_attr->show) + ret = channel_attr->show(dev, buf); + return ret; +} + +static ssize_t channel_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct channel_attribute *channel_attr = to_channel_attr(attr); + struct visor_device *dev = to_visor_device_from_kobjchannel(kobj); + ssize_t ret = 0; + + if (channel_attr->store) + ret = channel_attr->store(dev, buf, count); + return ret; +} + +static int channel_create_file(struct visor_device *dev, + struct channel_attribute *attr) +{ + return sysfs_create_file(&dev->kobjchannel, &attr->attr); +} + +static void channel_remove_file(struct visor_device *dev, + struct channel_attribute *attr) +{ + sysfs_remove_file(&dev->kobjchannel, &attr->attr); +} + +static const struct sysfs_ops channel_sysfs_ops = { + .show = channel_attr_show, + .store = channel_attr_store, +}; + +static struct kobj_type channel_kobj_type = { + .sysfs_ops = &channel_sysfs_ops +}; + +int register_channel_attributes(struct visor_device *dev) +{ + int rc = 0, i = 0, x = 0; + + if (dev->kobjchannel.parent) + goto away; /* already registered */ + x = kobject_init_and_add(&dev->kobjchannel, &channel_kobj_type, + &dev->device.kobj, "channel"); + if (x < 0) { + rc = x; + goto away; + } + + kobject_uevent(&dev->kobjchannel, KOBJ_ADD); + + for (i = 0; + i < sizeof(all_channel_attrs) / sizeof(struct channel_attribute); + i++) + x = channel_create_file(dev, &all_channel_attrs[i]); + if (x < 0) { + while (--i >= 0) + channel_remove_file(dev, &all_channel_attrs[i]); + kobject_del(&dev->kobjchannel); + kobject_put(&dev->kobjchannel); + rc = x; + goto away; + } +away: + return rc; +} + +void unregister_channel_attributes(struct visor_device *dev) +{ + int i = 0; + + if (!dev->kobjchannel.parent) + return; /* already unregistered */ + for (i = 0; + i < sizeof(all_channel_attrs) / sizeof(struct channel_attribute); + i++) + channel_remove_file(dev, &all_channel_attrs[i]); + + kobject_del(&dev->kobjchannel); + kobject_put(&dev->kobjchannel); + dev->kobjchannel.parent = NULL; +} diff --git a/drivers/staging/unisys/visorbus/channel_attr.h b/drivers/staging/unisys/visorbus/channel_attr.h new file mode 100644 index 000000000000..00ae37cb32ee --- /dev/null +++ b/drivers/staging/unisys/visorbus/channel_attr.h @@ -0,0 +1,27 @@ +/* channel_attr.h + * + * Copyright (C) 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __CHANNEL_ATTR_H__ +#define __CHANNEL_ATTR_H__ + +#include "visorbus.h" /* just to get visor_device declaration */ +#include "timskmod.h" + +int register_channel_attributes(struct visor_device *dev); +void unregister_channel_attributes(struct visor_device *dev); + +#endif diff --git a/drivers/staging/unisys/visorbus/devmajorminor_attr.c b/drivers/staging/unisys/visorbus/devmajorminor_attr.c new file mode 100644 index 000000000000..ceb4bb7394c6 --- /dev/null +++ b/drivers/staging/unisys/visorbus/devmajorminor_attr.c @@ -0,0 +1,192 @@ +/* devmajorminor_attr.c + * + * Copyright (C) 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. + */ + +/* Implement publishing of device node attributes under: + * + * /sys/bus/visorbus/dev/devmajorminor + * + */ + +#include "devmajorminor_attr.h" +#define CURRENT_FILE_PC VISOR_BUS_PC_devmajorminor_attr_c + +#define to_devmajorminor_attr(_attr) \ + container_of(_attr, struct devmajorminor_attribute, attr) +#define to_visor_device_from_kobjdevmajorminor(obj) \ + container_of(obj, struct visor_device, kobjdevmajorminor) + +struct devmajorminor_attribute { + struct attribute attr; + int slot; + ssize_t (*show)(struct visor_device *, int slot, char *buf); + ssize_t (*store)(struct visor_device *, int slot, const char *buf, + size_t count); +}; + +static ssize_t DEVMAJORMINOR_ATTR(struct visor_device *dev, int slot, char *buf) +{ + int maxdevnodes = ARRAY_SIZE(dev->devnodes) / sizeof(dev->devnodes[0]); + + if (slot < 0 || slot >= maxdevnodes) + return 0; + return snprintf(buf, PAGE_SIZE, "%d:%d\n", + dev->devnodes[slot].major, dev->devnodes[slot].minor); +} + +static ssize_t +devmajorminor_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct devmajorminor_attribute *devmajorminor_attr = + to_devmajorminor_attr(attr); + struct visor_device *dev = to_visor_device_from_kobjdevmajorminor(kobj); + ssize_t ret = 0; + + if (devmajorminor_attr->show) + ret = devmajorminor_attr->show(dev, + devmajorminor_attr->slot, buf); + return ret; +} + +static ssize_t +devmajorminor_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct devmajorminor_attribute *devmajorminor_attr = + to_devmajorminor_attr(attr); + struct visor_device *dev = to_visor_device_from_kobjdevmajorminor(kobj); + ssize_t ret = 0; + + if (devmajorminor_attr->store) + ret = devmajorminor_attr->store(dev, + devmajorminor_attr->slot, + buf, count); + return ret; +} + +int +devmajorminor_create_file(struct visor_device *dev, const char *name, + int major, int minor) +{ + int maxdevnodes = ARRAY_SIZE(dev->devnodes) / sizeof(dev->devnodes[0]); + struct devmajorminor_attribute *myattr = NULL; + int x = -1, rc = 0, slot = -1; + + register_devmajorminor_attributes(dev); + for (slot = 0; slot < maxdevnodes; slot++) + if (!dev->devnodes[slot].attr) + break; + if (slot == maxdevnodes) { + rc = -ENOMEM; + goto away; + } + myattr = kmalloc(sizeof(*myattr), GFP_KERNEL); + if (!myattr) { + rc = -ENOMEM; + goto away; + } + memset(myattr, 0, sizeof(struct devmajorminor_attribute)); + myattr->show = DEVMAJORMINOR_ATTR; + myattr->store = NULL; + myattr->slot = slot; + myattr->attr.name = name; + myattr->attr.mode = S_IRUGO; + dev->devnodes[slot].attr = myattr; + dev->devnodes[slot].major = major; + dev->devnodes[slot].minor = minor; + x = sysfs_create_file(&dev->kobjdevmajorminor, &myattr->attr); + if (x < 0) { + rc = x; + goto away; + } + kobject_uevent(&dev->device.kobj, KOBJ_ONLINE); +away: + if (rc < 0) { + kfree(myattr); + myattr = NULL; + dev->devnodes[slot].attr = NULL; + } + return rc; +} + +void +devmajorminor_remove_file(struct visor_device *dev, int slot) +{ + int maxdevnodes = ARRAY_SIZE(dev->devnodes) / sizeof(dev->devnodes[0]); + struct devmajorminor_attribute *myattr = NULL; + + if (slot < 0 || slot >= maxdevnodes) + return; + myattr = (struct devmajorminor_attribute *)(dev->devnodes[slot].attr); + if (myattr) + return; + sysfs_remove_file(&dev->kobjdevmajorminor, &myattr->attr); + kobject_uevent(&dev->device.kobj, KOBJ_OFFLINE); + dev->devnodes[slot].attr = NULL; + kfree(myattr); +} + +void +devmajorminor_remove_all_files(struct visor_device *dev) +{ + int i = 0; + int maxdevnodes = ARRAY_SIZE(dev->devnodes) / sizeof(dev->devnodes[0]); + + for (i = 0; i < maxdevnodes; i++) + devmajorminor_remove_file(dev, i); +} + +static const struct sysfs_ops devmajorminor_sysfs_ops = { + .show = devmajorminor_attr_show, + .store = devmajorminor_attr_store, +}; + +static struct kobj_type devmajorminor_kobj_type = { + .sysfs_ops = &devmajorminor_sysfs_ops +}; + +int +register_devmajorminor_attributes(struct visor_device *dev) +{ + int rc = 0, x = 0; + + if (dev->kobjdevmajorminor.parent) + goto away; /* already registered */ + x = kobject_init_and_add(&dev->kobjdevmajorminor, + &devmajorminor_kobj_type, &dev->device.kobj, + "devmajorminor"); + if (x < 0) { + rc = x; + goto away; + } + + kobject_uevent(&dev->kobjdevmajorminor, KOBJ_ADD); + +away: + return rc; +} + +void +unregister_devmajorminor_attributes(struct visor_device *dev) +{ + if (!dev->kobjdevmajorminor.parent) + return; /* already unregistered */ + devmajorminor_remove_all_files(dev); + + kobject_del(&dev->kobjdevmajorminor); + kobject_put(&dev->kobjdevmajorminor); + dev->kobjdevmajorminor.parent = NULL; +} diff --git a/drivers/staging/unisys/visorbus/devmajorminor_attr.h b/drivers/staging/unisys/visorbus/devmajorminor_attr.h new file mode 100644 index 000000000000..0b55cb1df46a --- /dev/null +++ b/drivers/staging/unisys/visorbus/devmajorminor_attr.h @@ -0,0 +1,31 @@ +/* devmajorminor_attr.h + * + * Copyright (C) 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __DEVMAJORMINOR_ATTR_H__ +#define __DEVMAJORMINOR_ATTR_H__ + +#include "visorbus.h" /* just to get visor_device declaration */ +#include "timskmod.h" + +int register_devmajorminor_attributes(struct visor_device *dev); +void unregister_devmajorminor_attributes(struct visor_device *dev); +int devmajorminor_create_file(struct visor_device *dev, const char *nam, + int major, int minor); +void devmajorminor_remove_file(struct visor_device *dev, int slot); +void devmajorminor_remove_all_files(struct visor_device *dev); + +#endif diff --git a/drivers/staging/unisys/visorbus/visorbus.h b/drivers/staging/unisys/visorbus/visorbus.h new file mode 100644 index 000000000000..856fa3f6419c --- /dev/null +++ b/drivers/staging/unisys/visorbus/visorbus.h @@ -0,0 +1,166 @@ +/* visorbus.h + * + * Copyright (C) 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. + */ + +/* + * This header file is to be included by other kernel mode components that + * implement a particular kind of visor_device. Each of these other kernel + * mode components is called a visor device driver. Refer to visortemplate + * for a minimal sample visor device driver. + * + * There should be nothing in this file that is private to the visorbus + * bus implementation itself. + * + */ + +#ifndef __VISORBUS_H__ +#define __VISORBUS_H__ + +#include +#include +#include +#include + +#include "periodic_work.h" +#include "visorchannel.h" +#include "channel.h" + +struct visor_driver; +struct visor_device; + +typedef void (*visorbus_state_complete_func) (struct visor_device *dev, + int status); + +/** This struct describes a specific Supervisor channel, by providing its + * GUID, name, and sizes. + */ +struct visor_channeltype_descriptor { + const uuid_le guid; + const char *name; + unsigned long min_size; + unsigned long max_size; +}; + +/** Information provided by each visor driver when it registers with the + * visorbus driver. + */ +struct visor_driver { + const char *name; + const char *version; + const char *vertag; + const char *build_date; + const char *build_time; + struct module *owner; + + /** Types of channels handled by this driver, ending with 0 GUID. + * Our specialized BUS.match() method knows about this list, and + * uses it to determine whether this driver will in fact handle a + * new device that it has detected. + */ + struct visor_channeltype_descriptor *channel_types; + + /** Called when a new device comes online, by our probe() function + * specified by driver.probe() (triggered ultimately by some call + * to driver_register() / bus_add_driver() / driver_attach()). + */ + int (*probe)(struct visor_device *dev); + + /** Called when a new device is removed, by our remove() function + * specified by driver.remove() (triggered ultimately by some call + * to device_release_driver()). + */ + void (*remove)(struct visor_device *dev); + + /** Called periodically, whenever there is a possibility that + * "something interesting" may have happened to the channel state. + */ + void (*channel_interrupt)(struct visor_device *dev); + + /** Called to initiate a change of the device's state. If the return + * valu`e is < 0, there was an error and the state transition will NOT + * occur. If the return value is >= 0, then the state transition was + * INITIATED successfully, and complete_func() will be called (or was + * just called) with the final status when either the state transition + * fails or completes successfully. + */ + int (*pause)(struct visor_device *dev, + visorbus_state_complete_func complete_func); + int (*resume)(struct visor_device *dev, + visorbus_state_complete_func complete_func); + + /** These fields are for private use by the bus driver only. */ + struct device_driver driver; + struct driver_attribute version_attr; +}; + +#define to_visor_driver(x) container_of(x, struct visor_driver, driver) + +/** A device type for things "plugged" into the visorbus bus */ + +struct visor_device { + /** visor driver can use the visorchannel member with the functions + * defined in visorchannel.h to access the channel + */ + struct visorchannel *visorchannel; + uuid_le channel_type_guid; + u64 channel_bytes; + + /** These fields are for private use by the bus driver only. + * A notable exception is that the visor driver can use + * visor_get_drvdata() and visor_set_drvdata() to retrieve or stash + * private visor driver specific data within the device member. + */ + struct device device; + struct list_head list_all; + struct periodic_work *periodic_work; + bool being_removed; + bool responded_to_device_create; + struct kobject kobjchannel; /* visorbus/dev/channel/ */ + struct kobject kobjdevmajorminor; /* visorbus/dev/devmajorminor/*/ + struct { + int major, minor; + void *attr; /* private use by devmajorminor_attr.c you can + * change this constant to whatever you + * want; */ + } devnodes[5]; + /* the code will detect and behave appropriately) */ + struct semaphore visordriver_callback_lock; + bool pausing; + bool resuming; + unsigned long chipset_bus_no; + unsigned long chipset_dev_no; +}; + +#define to_visor_device(x) container_of(x, struct visor_device, device) + +#ifndef STANDALONE_CLIENT +int visorbus_register_visor_driver(struct visor_driver *); +void visorbus_unregister_visor_driver(struct visor_driver *); +int visorbus_read_channel(struct visor_device *dev, + unsigned long offset, void *dest, + unsigned long nbytes); +int visorbus_write_channel(struct visor_device *dev, + unsigned long offset, void *src, + unsigned long nbytes); +int visorbus_clear_channel(struct visor_device *dev, + unsigned long offset, u8 ch, unsigned long nbytes); +int visorbus_registerdevnode(struct visor_device *dev, + const char *name, int major, int minor); +void visorbus_enable_channel_interrupts(struct visor_device *dev); +void visorbus_disable_channel_interrupts(struct visor_device *dev); +#endif + +#endif diff --git a/drivers/staging/unisys/visorbus/visorbus_main.c b/drivers/staging/unisys/visorbus/visorbus_main.c new file mode 100644 index 000000000000..d717e35d5a1e --- /dev/null +++ b/drivers/staging/unisys/visorbus/visorbus_main.c @@ -0,0 +1,1643 @@ +/* visorbus_main.c + * + * Copyright � 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#include + +#include "visorbus_private.h" +#include "businst_attr.h" +#include "channel_attr.h" +#include "devmajorminor_attr.h" +#include "periodic_work.h" +#include "vbuschannel.h" +#include "guestlinuxdebug.h" +#include "vbusdeviceinfo.h" +/* These forward declarations are required since our drivers are out-of-tree. + * The structures referenced are kernel-private and are not in the headers, but + * it is impossible to make a functioning bus driver without them. + */ +struct subsys_private { + struct kset subsys; + struct kset *devices_kset; + + struct kset *drivers_kset; + struct klist klist_devices; + struct klist klist_drivers; + struct blocking_notifier_head bus_notifier; + unsigned int drivers_autoprobe:1; + struct bus_type *bus; + + struct list_head class_interfaces; + struct kset glue_dirs; + struct mutex class_mutex; /* ignore */ + struct class *class; +}; + +struct bus_type_private { + struct kset subsys; + struct kset *drivers_kset; + struct kset *devices_kset; + struct klist klist_devices; + struct klist klist_drivers; + struct blocking_notifier_head bus_notifier; + unsigned int drivers_autoprobe:1; + struct bus_type *bus; +}; + +#define CURRENT_FILE_PC VISOR_BUS_PC_visorbus_main_c +#define POLLJIFFIES_TESTWORK 100 +#define POLLJIFFIES_NORMALCHANNEL 10 + +static int visorbus_uevent(struct device *xdev, struct kobj_uevent_env *env); +static int visorbus_match(struct device *xdev, struct device_driver *xdrv); +static void fix_vbus_dev_info(struct visor_device *visordev); + +/** This describes the TYPE of bus. + * (Don't confuse this with an INSTANCE of the bus.) + */ +static struct bus_type visorbus_type = { + .name = "visorbus", + .match = visorbus_match, + .uevent = visorbus_uevent, +}; + +static struct delayed_work periodic_work; + +/* YES, we need 2 workqueues. + * The reason is, workitems on the test queue may need to cancel + * workitems on the other queue. You will be in for trouble if you try to + * do this with workitems queued on the same workqueue. + */ +static struct workqueue_struct *periodic_test_workqueue; +static struct workqueue_struct *periodic_dev_workqueue; +static long long bus_count; /** number of bus instances */ +static long long total_devices_created; + /** ever-increasing */ + +static void chipset_bus_create(u32 bus_no); +static void chipset_bus_destroy(u32 bus_no); +static void chipset_device_create(u32 bus_no, u32 dev_no); +static void chipset_device_destroy(u32 bus_no, u32 dev_no); +static void chipset_device_pause(u32 bus_no, u32 dev_no); +static void chipset_device_resume(u32 bus_no, u32 dev_no); + +/** These functions are implemented herein, and are called by the chipset + * driver to notify us about specific events. + */ +static struct visorchipset_busdev_notifiers chipset_notifiers = { + .bus_create = chipset_bus_create, + .bus_destroy = chipset_bus_destroy, + .device_create = chipset_device_create, + .device_destroy = chipset_device_destroy, + .device_pause = chipset_device_pause, + .device_resume = chipset_device_resume, +}; + +/** These functions are implemented in the chipset driver, and we call them + * herein when we want to acknowledge a specific event. + */ +static struct visorchipset_busdev_responders chipset_responders; + +/* filled in with info about parent chipset driver when we register with it */ +static struct ultra_vbus_deviceinfo chipset_driverinfo; +/* filled in with info about this driver, wrt it servicing client busses */ +static struct ultra_vbus_deviceinfo clientbus_driverinfo; + +/** list of visorbus_devdata structs, linked via .list_all */ +static LIST_HEAD(list_all_bus_instances); +/** list of visor_device structs, linked via .list_all */ +static LIST_HEAD(list_all_device_instances); + +static int +visorbus_uevent(struct device *xdev, struct kobj_uevent_env *env) +{ + if (add_uevent_var(env, "VERSION=%s", VERSION)) + return -ENOMEM; + return 0; +} + +/* This is called automatically upon adding a visor_device (device_add), or + * adding a visor_driver (visorbus_register_visor_driver), and returns 1 iff the + * provided driver can control the specified device. + */ +static int +visorbus_match(struct device *xdev, struct device_driver *xdrv) +{ + uuid_le channel_type; + int rc = 0; + int i; + struct visor_device *dev; + struct visor_driver *drv; + + dev = to_visor_device(xdev); + drv = to_visor_driver(xdrv); + channel_type = visorchannel_get_uuid(dev->visorchannel); + if (visorbus_forcematch) { + rc = 1; + goto away; + } + if (visorbus_forcenomatch) + goto away; + + if (!drv->channel_types) + goto away; + for (i = 0; + (uuid_le_cmp(drv->channel_types[i].guid, NULL_UUID_LE) != 0) || + (drv->channel_types[i].name); + i++) + if (uuid_le_cmp(drv->channel_types[i].guid, + channel_type) == 0) { + rc = i + 1; + goto away; + } +away: + return rc; +} + +/** This is called when device_unregister() is called for the bus device + * instance, after all other tasks involved with destroying the device + * are complete. + */ +static void +visorbus_release_busdevice(struct device *xdev) +{ + struct visorbus_devdata *devdata = dev_get_drvdata(xdev); + + dev_set_drvdata(xdev, NULL); + kfree(devdata); + kfree(xdev); +} + +/** This is called when device_unregister() is called for each child + * device instance. + */ +static void +visorbus_release_device(struct device *xdev) +{ + struct visor_device *dev = to_visor_device(xdev); + + if (dev->periodic_work) { + visor_periodic_work_destroy(dev->periodic_work); + dev->periodic_work = NULL; + } + if (dev->visorchannel) { + visorchannel_destroy(dev->visorchannel); + dev->visorchannel = NULL; + } + kfree(dev); +} + +static const struct sysfs_ops businst_sysfs_ops = { + .show = businst_attr_show, + .store = businst_attr_store, +}; + +static struct kobj_type businst_kobj_type = { + .sysfs_ops = &businst_sysfs_ops +}; + +static struct kset businstances = { /* should actually be a member of + * bus_type */ +}; + +/* BUS type attributes + * + * define & implement display of bus attributes under + * /sys/bus/visorbus. + * + */ + +static ssize_t +BUSTYPE_ATTR_version(struct bus_type *bus, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VERSION); +} + +static struct bus_attribute bustype_attr_version = +__ATTR(version, S_IRUGO, BUSTYPE_ATTR_version, NULL); + +static int +register_bustype_attributes(void) +{ + int rc = 0; + + rc = bus_create_file(&visorbus_type, &bustype_attr_version); + if (rc < 0) + goto away; + + /* Here we make up for the fact that bus_type does not yet have a + * member to keep track of multiple bus instances for a given bus + * type. This is useful for stashing properties for each bus + * instance. + */ + kobject_set_name(&businstances.kobj, "busses"); + businstances.kobj.ktype = &businst_kobj_type; + businstances.kobj.parent = &visorbus_type.p->subsys.kobj; + rc = kset_register(&businstances); + if (rc < 0) + goto away; + + rc = 0; +away: + return rc; +} + +static void +unregister_bustype_attributes(void) +{ + bus_remove_file(&visorbus_type, &bustype_attr_version); + kset_unregister(&businstances); +} + +/* BUS instance attributes + * + * define & implement display of bus attributes under + * /sys/bus/visorbus/busses/visorbus. + * + * This is a bit hoaky because the kernel does not yet have the infrastructure + * to separate bus INSTANCE attributes from bus TYPE attributes... + * so we roll our own. See businst.c / businst.h. + * + */ + +static ssize_t businst_attr_partition_handle(struct visorbus_devdata *businst, + char *buf) { + struct visorchipset_bus_info bus_info; + int len = 0; + + if (businst && visorchipset_get_bus_info(businst->devno, &bus_info)) + len = snprintf(buf, PAGE_SIZE, + "0x%Lx\n", + (unsigned long long)bus_info.partition_handle); + return len; +} + +static ssize_t businst_attr_partition_guid(struct visorbus_devdata *businst, + char *buf) { + struct visorchipset_bus_info bus_info; + int len = 0; + + if (businst && visorchipset_get_bus_info(businst->devno, &bus_info)) + len = snprintf(buf, PAGE_SIZE, "{%pUb}\n", + &bus_info.partition_uuid); + return len; +} + +static ssize_t businst_attr_partition_name(struct visorbus_devdata *businst, + char *buf) { + struct visorchipset_bus_info bus_info; + int len = 0; + + if (businst && + visorchipset_get_bus_info(businst->devno, &bus_info) && + bus_info.name) + len = snprintf(buf, PAGE_SIZE, "%s\n", bus_info.name); + return len; +} + +static ssize_t businst_attr_channel_addr(struct visorbus_devdata *businst, + char *buf) { + struct visorchipset_bus_info bus_info; + int len = 0; + + if (businst && visorchipset_get_bus_info(businst->devno, &bus_info)) + len = snprintf(buf, PAGE_SIZE, "0x%Lx\n", (unsigned long long) + bus_info.chan_info.channel_addr); + return len; +} + +static ssize_t businst_attr_nchannel_bytes(struct visorbus_devdata *businst, + char *buf) { + struct visorchipset_bus_info bus_info; + int len = 0; + + if (businst && visorchipset_get_bus_info(businst->devno, &bus_info)) + len = snprintf(buf, PAGE_SIZE, "0x%Lx\n", (unsigned long long) + bus_info.chan_info.n_channel_bytes); + return len; +} + +static ssize_t businst_attr_channel_id(struct visorbus_devdata *businst, + char *buf) { + int len = 0; + + if (businst && businst->chan) { + visorchannel_id(businst->chan, buf); + len = strlen(buf); + buf[len++] = '\n'; + } + return len; +} + +static ssize_t businst_attr_client_bus_info(struct visorbus_devdata *businst, + char *buf) { + struct visorchipset_bus_info bus_info; + int i, x, remain = PAGE_SIZE; + unsigned long off; + char *p = buf; + u8 *partition_name; + struct ultra_vbus_deviceinfo dev_info; + + partition_name = ""; + if (businst && businst->chan) { + if (visorchipset_get_bus_info(businst->devno, &bus_info) && + bus_info.name) + partition_name = bus_info.name; + x = snprintf(p, remain, + "Client device / client driver info for %s partition (vbus #%d):\n", + partition_name, businst->devno); + p += x; + remain -= x; + x = visorchannel_read(businst->chan, + offsetof(struct + spar_vbus_channel_protocol, + chp_info), + &dev_info, sizeof(dev_info)); + if (x >= 0) { + x = vbuschannel_devinfo_to_string(&dev_info, p, + remain, -1); + p += x; + remain -= x; + } + x = visorchannel_read(businst->chan, + offsetof(struct + spar_vbus_channel_protocol, + bus_info), + &dev_info, sizeof(dev_info)); + if (x >= 0) { + x = vbuschannel_devinfo_to_string(&dev_info, p, + remain, -1); + p += x; + remain -= x; + } + off = offsetof(struct spar_vbus_channel_protocol, dev_info); + i = 0; + while (off + sizeof(dev_info) <= + visorchannel_get_nbytes(businst->chan)) { + x = visorchannel_read(businst->chan, + off, &dev_info, sizeof(dev_info)); + if (x >= 0) { + x = vbuschannel_devinfo_to_string + (&dev_info, p, remain, i); + p += x; + remain -= x; + } + off += sizeof(dev_info); + i++; + } + } + return PAGE_SIZE - remain; +} + +static struct businst_attribute ba_partition_handle = + __ATTR(partition_handle, S_IRUGO, businst_attr_partition_handle, NULL); +static struct businst_attribute ba_partition_guid = + __ATTR(partition_guid, S_IRUGO, businst_attr_partition_guid, NULL); +static struct businst_attribute ba_partition_name = + __ATTR(partition_name, S_IRUGO, businst_attr_partition_name, NULL); +static struct businst_attribute ba_channel_addr = + __ATTR(channel_addr, S_IRUGO, businst_attr_channel_addr, NULL); +static struct businst_attribute ba_nchannel_bytes = + __ATTR(nchannel_bytes, S_IRUGO, businst_attr_nchannel_bytes, NULL); +static struct businst_attribute ba_channel_id = + __ATTR(channel_id, S_IRUGO, businst_attr_channel_id, NULL); +static struct businst_attribute ba_client_bus_info = + __ATTR(client_bus_info, S_IRUGO, businst_attr_client_bus_info, NULL); + +static int +register_businst_attributes(struct visorbus_devdata *businst) +{ + int rc = 0; + + businst->kobj.kset = &businstances; /* identify parent sysfs dir */ + rc = kobject_init_and_add(&businst->kobj, &businst_kobj_type, + NULL, "visorbus%d", businst->devno); + if (rc < 0) + goto away; + + rc = businst_create_file(businst, &ba_partition_handle); + if (rc < 0) + goto away; + + rc = businst_create_file(businst, &ba_partition_guid); + if (rc < 0) + goto away; + + rc = businst_create_file(businst, &ba_partition_name); + if (rc < 0) + goto away; + + rc = businst_create_file(businst, &ba_channel_addr); + if (rc < 0) + goto away; + + rc = businst_create_file(businst, &ba_nchannel_bytes); + if (rc < 0) + goto away; + + rc = businst_create_file(businst, &ba_channel_id); + if (rc < 0) + goto away; + + rc = businst_create_file(businst, &ba_client_bus_info); + if (rc < 0) + goto away; + + kobject_uevent(&businst->kobj, KOBJ_ADD); + + rc = 0; +away: + return rc; +} + +static void +unregister_businst_attributes(struct visorbus_devdata *businst) +{ + businst_remove_file(businst, &ba_partition_handle); + businst_remove_file(businst, &ba_partition_guid); + businst_remove_file(businst, &ba_partition_name); + businst_remove_file(businst, &ba_channel_addr); + businst_remove_file(businst, &ba_nchannel_bytes); + businst_remove_file(businst, &ba_channel_id); + businst_remove_file(businst, &ba_client_bus_info); + kobject_put(&businst->kobj); +} + +/* DRIVER attributes + * + * define & implement display of driver attributes under + * /sys/bus/visorbus/drivers/. + * + */ + +static ssize_t +DRIVER_ATTR_version(struct device_driver *xdrv, char *buf) +{ + struct visor_driver *drv = to_visor_driver(xdrv); + + return snprintf(buf, PAGE_SIZE, "%s\n", drv->version); +} + +static int +register_driver_attributes(struct visor_driver *drv) +{ + int rc; + struct driver_attribute version = + __ATTR(version, S_IRUGO, DRIVER_ATTR_version, NULL); + drv->version_attr = version; + rc = driver_create_file(&drv->driver, &drv->version_attr); + return rc; +} + +static void +unregister_driver_attributes(struct visor_driver *drv) +{ + driver_remove_file(&drv->driver, &drv->version_attr); +} + +/* DEVICE attributes + * + * define & implement display of device attributes under + * /sys/bus/visorbus/devices/. + * + */ + +#define DEVATTR(nam, func) { \ + .attr = { .name = __stringify(nam), \ + .mode = 0444, \ + .owner = THIS_MODULE }, \ + .show = func, \ +} + +static struct device_attribute visor_device_attrs[] = { + /* DEVATTR(channel_nbytes, DEVICE_ATTR_channel_nbytes), */ + __ATTR_NULL +}; + +static void +dev_periodic_work(void *xdev) +{ + struct visor_device *dev = (struct visor_device *)xdev; + struct visor_driver *drv = to_visor_driver(dev->device.driver); + + down(&dev->visordriver_callback_lock); + if (drv->channel_interrupt) + drv->channel_interrupt(dev); + up(&dev->visordriver_callback_lock); + if (!visor_periodic_work_nextperiod(dev->periodic_work)) + put_device(&dev->device); +} + +static void +dev_start_periodic_work(struct visor_device *dev) +{ + if (dev->being_removed) + return; + /* now up by at least 2 */ + get_device(&dev->device); + if (!visor_periodic_work_start(dev->periodic_work)) + put_device(&dev->device); +} + +static void +dev_stop_periodic_work(struct visor_device *dev) +{ + if (visor_periodic_work_stop(dev->periodic_work)) + put_device(&dev->device); +} + +/** This is called automatically upon adding a visor_device (device_add), or + * adding a visor_driver (visorbus_register_visor_driver), but only after + * visorbus_match has returned 1 to indicate a successful match between + * driver and device. + */ +static int +visordriver_probe_device(struct device *xdev) +{ + int rc; + struct visor_driver *drv; + struct visor_device *dev; + + drv = to_visor_driver(xdev->driver); + dev = to_visor_device(xdev); + down(&dev->visordriver_callback_lock); + dev->being_removed = FALSE; + /* + * ensure that the dev->being_removed flag is cleared before + * we start the probe + */ + wmb(); + get_device(&dev->device); + if (!drv->probe) { + up(&dev->visordriver_callback_lock); + rc = -1; + goto away; + } + rc = drv->probe(dev); + if (rc < 0) + goto away; + + fix_vbus_dev_info(dev); + up(&dev->visordriver_callback_lock); + rc = 0; +away: + if (rc != 0) + put_device(&dev->device); + /* We could get here more than once if the child driver module is + * unloaded and re-loaded while devices are present. That's why we + * need a flag to be sure that we only respond to the device_create + * once. We cannot respond to the device_create prior to here, + * because until we call drv->probe() above, the channel has not been + * initialized. + */ + if (!dev->responded_to_device_create) { + dev->responded_to_device_create = TRUE; + if (chipset_responders.device_create) + (*chipset_responders.device_create)(dev->chipset_bus_no, + dev->chipset_dev_no, + rc); + } + return rc; +} + +/** This is called when device_unregister() is called for each child device + * instance, to notify the appropriate visorbus_driver that the device is + * going away, and to decrease the reference count of the device. + */ +static int +visordriver_remove_device(struct device *xdev) +{ + int rc = 0; + struct visor_device *dev; + struct visor_driver *drv; + + dev = to_visor_device(xdev); + drv = to_visor_driver(xdev->driver); + down(&dev->visordriver_callback_lock); + dev->being_removed = TRUE; + /* + * ensure that the dev->being_removed flag is set before we start the + * actual removal + */ + wmb(); + if (drv) { + if (drv->remove) + drv->remove(dev); + } + up(&dev->visordriver_callback_lock); + dev_stop_periodic_work(dev); + devmajorminor_remove_all_files(dev); + + put_device(&dev->device); + + return rc; +} + +/** A particular type of visor driver calls this function to register + * the driver. The caller MUST fill in the following fields within the + * #drv structure: + * name, version, owner, channel_types, probe, remove + * + * Here's how the whole Linux bus / driver / device model works. + * + * At system start-up, the visorbus kernel module is loaded, which registers + * visorbus_type as a bus type, using bus_register(). + * + * All kernel modules that support particular device types on a + * visorbus bus are loaded. Each of these kernel modules calls + * visorbus_register_visor_driver() in their init functions, passing a + * visor_driver struct. visorbus_register_visor_driver() in turn calls + * register_driver(&visor_driver.driver). This .driver member is + * initialized with generic methods (like probe), whose sole responsibility + * is to act as a broker for the real methods, which are within the + * visor_driver struct. (This is the way the subclass behavior is + * implemented, since visor_driver is essentially a subclass of the + * generic driver.) Whenever a driver_register() happens, core bus code in + * the kernel does (see device_attach() in drivers/base/dd.c): + * + * for each dev associated with the bus (the bus that driver is on) that + * does not yet have a driver + * if bus.match(dev,newdriver) == yes_matched ** .match specified + * ** during bus_register(). + * newdriver.probe(dev) ** for visor drivers, this will call + * ** the generic driver.probe implemented in visorbus.c, + * ** which in turn calls the probe specified within the + * ** struct visor_driver (which was specified by the + * ** actual device driver as part of + * ** visorbus_register_visor_driver()). + * + * The above dance also happens when a new device appears. + * So the question is, how are devices created within the system? + * Basically, just call device_add(dev). See pci_bus_add_devices(). + * pci_scan_device() shows an example of how to build a device struct. It + * returns the newly-created struct to pci_scan_single_device(), who adds it + * to the list of devices at PCIBUS.devices. That list of devices is what + * is traversed by pci_bus_add_devices(). + * + */ +int visorbus_register_visor_driver(struct visor_driver *drv) +{ + int rc = 0; + + drv->driver.name = drv->name; + drv->driver.bus = &visorbus_type; + drv->driver.probe = visordriver_probe_device; + drv->driver.remove = visordriver_remove_device; + drv->driver.owner = drv->owner; + + /* driver_register does this: + * bus_add_driver(drv) + * ->if (drv.bus) ** (bus_type) ** + * driver_attach(drv) + * for each dev with bus type of drv.bus + * if (!dev.drv) ** no driver assigned yet ** + * if (bus.match(dev,drv)) [visorbus_match] + * dev.drv = drv + * if (!drv.probe(dev)) [visordriver_probe_device] + * dev.drv = NULL + */ + + rc = driver_register(&drv->driver); + if (rc < 0) + return rc; + rc = register_driver_attributes(drv); + return rc; +} +EXPORT_SYMBOL_GPL(visorbus_register_visor_driver); + +/** A particular type of visor driver calls this function to unregister + * the driver, i.e., within its module_exit function. + */ +void +visorbus_unregister_visor_driver(struct visor_driver *drv) +{ + unregister_driver_attributes(drv); + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(visorbus_unregister_visor_driver); + +int +visorbus_read_channel(struct visor_device *dev, unsigned long offset, + void *dest, unsigned long nbytes) +{ + return visorchannel_read(dev->visorchannel, offset, dest, nbytes); +} +EXPORT_SYMBOL_GPL(visorbus_read_channel); + +int +visorbus_write_channel(struct visor_device *dev, unsigned long offset, + void *src, unsigned long nbytes) +{ + return visorchannel_write(dev->visorchannel, offset, src, nbytes); +} +EXPORT_SYMBOL_GPL(visorbus_write_channel); + +int +visorbus_clear_channel(struct visor_device *dev, unsigned long offset, u8 ch, + unsigned long nbytes) +{ + return visorchannel_clear(dev->visorchannel, offset, ch, nbytes); +} +EXPORT_SYMBOL_GPL(visorbus_clear_channel); + +int +visorbus_registerdevnode(struct visor_device *dev, + const char *name, int major, int minor) +{ + return devmajorminor_create_file(dev, name, major, minor); +} +EXPORT_SYMBOL_GPL(visorbus_registerdevnode); + +/** We don't really have a real interrupt, so for now we just call the + * interrupt function periodically... + */ +void +visorbus_enable_channel_interrupts(struct visor_device *dev) +{ + dev_start_periodic_work(dev); +} +EXPORT_SYMBOL_GPL(visorbus_enable_channel_interrupts); + +void +visorbus_disable_channel_interrupts(struct visor_device *dev) +{ + dev_stop_periodic_work(dev); +} +EXPORT_SYMBOL_GPL(visorbus_disable_channel_interrupts); + +/** This is how everything starts from the device end. + * This function is called when a channel first appears via a ControlVM + * message. In response, this function allocates a visor_device to + * correspond to the new channel, and attempts to connect it the appropriate + * driver. If the appropriate driver is found, the visor_driver.probe() + * function for that driver will be called, and will be passed the new + * visor_device that we just created. + * + * It's ok if the appropriate driver is not yet loaded, because in that case + * the new device struct will just stick around in the bus' list of devices. + * When the appropriate driver calls visorbus_register_visor_driver(), the + * visor_driver.probe() for the new driver will be called with the new + * device. + */ +static int +create_visor_device(struct visorbus_devdata *devdata, + unsigned long chipset_bus_no, unsigned long chipset_dev_no, + struct visorchipset_channel_info chan_info, + u64 partition_handle) +{ + int rc = -1; + struct visorchannel *visorchannel = NULL; + struct visor_device *dev = NULL; + bool gotten = FALSE, registered1 = FALSE, registered2 = FALSE; + + POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, chipset_dev_no, chipset_bus_no, + POSTCODE_SEVERITY_INFO); + /* prepare chan_hdr (abstraction to read/write channel memory) */ + visorchannel = visorchannel_create(chan_info.channel_addr, + (unsigned long) + chan_info.n_channel_bytes, + chan_info.channel_type_uuid); + if (!visorchannel) { + POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, chipset_dev_no, + DIAG_SEVERITY_ERR); + goto away; + } + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, chipset_dev_no, + DIAG_SEVERITY_ERR); + goto away; + } + + memset(dev, 0, sizeof(struct visor_device)); + dev->visorchannel = visorchannel; + dev->channel_type_guid = chan_info.channel_type_uuid; + dev->channel_bytes = chan_info.n_channel_bytes; + dev->chipset_bus_no = chipset_bus_no; + dev->chipset_dev_no = chipset_dev_no; + dev->device.parent = devdata->dev; + sema_init(&dev->visordriver_callback_lock, 1); /* unlocked */ + dev->device.bus = &visorbus_type; + device_initialize(&dev->device); + dev->device.release = visorbus_release_device; + /* keep a reference just for us (now 2) */ + get_device(&dev->device); + gotten = TRUE; + dev->periodic_work = + visor_periodic_work_create(POLLJIFFIES_NORMALCHANNEL, + periodic_dev_workqueue, + dev_periodic_work, + dev, dev_name(&dev->device)); + if (!dev->periodic_work) { + POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, chipset_dev_no, + DIAG_SEVERITY_ERR); + goto away; + } + + /* bus_id must be a unique name with respect to this bus TYPE + * (NOT bus instance). That's why we need to include the bus + * number within the name. + */ + dev_set_name(&dev->device, "vbus%lu:dev%lu", + chipset_bus_no, chipset_dev_no); + + /* device_add does this: + * bus_add_device(dev) + * ->device_attach(dev) + * ->for each driver drv registered on the bus that dev is on + * if (dev.drv) ** device already has a driver ** + * ** not sure we could ever get here... ** + * else + * if (bus.match(dev,drv)) [visorbus_match] + * dev.drv = drv + * if (!drv.probe(dev)) [visordriver_probe_device] + * dev.drv = NULL + * + * Note that device_add does NOT fail if no driver failed to + * claim the device. The device will be linked onto + * bus_type.klist_devices regardless (use bus_for_each_dev). + */ + rc = device_add(&dev->device); + if (rc < 0) { + POSTCODE_LINUX_3(DEVICE_ADD_PC, chipset_bus_no, + DIAG_SEVERITY_ERR); + goto away; + } + + /* note: device_register is simply device_initialize + device_add */ + rc = register_channel_attributes(dev); + if (rc < 0) { + POSTCODE_LINUX_3(DEVICE_REGISTER_FAILURE_PC, chipset_dev_no, + DIAG_SEVERITY_ERR); + goto away; + } + + registered1 = TRUE; + + rc = register_devmajorminor_attributes(dev); + if (rc < 0) { + POSTCODE_LINUX_3(DEVICE_REGISTER_FAILURE_PC, chipset_dev_no, + DIAG_SEVERITY_ERR); + goto away; + } + + registered2 = TRUE; + rc = 0; + +away: + if (rc < 0) { + if (registered2) + unregister_devmajorminor_attributes(dev); + if (registered1) + unregister_channel_attributes(dev); + if (gotten) + put_device(&dev->device); + if (visorchannel) + visorchannel_destroy(visorchannel); + kfree(dev); + } else { + total_devices_created++; + list_add_tail(&dev->list_all, &list_all_device_instances); + } + return rc; +} + +static void +remove_visor_device(struct visor_device *dev) +{ + list_del(&dev->list_all); + unregister_devmajorminor_attributes(dev); + unregister_channel_attributes(dev); + put_device(&dev->device); + device_unregister(&dev->device); +} + +static struct visor_device * +find_visor_device_by_channel(HOSTADDRESS channel_physaddr) +{ + struct list_head *listentry, *listtmp; + + list_for_each_safe(listentry, listtmp, &list_all_device_instances) { + struct visor_device *dev = list_entry(listentry, + struct visor_device, + list_all); + if (visorchannel_get_physaddr(dev->visorchannel) == + channel_physaddr) + return dev; + } + return NULL; +} + +static int +init_vbus_channel(struct visorchannel *chan) +{ + int rc = -1; + unsigned long allocated_bytes = visorchannel_get_nbytes(chan); + struct spar_vbus_channel_protocol *x = + kmalloc(sizeof(struct spar_vbus_channel_protocol), + GFP_KERNEL); + + POSTCODE_LINUX_3(VBUS_CHANNEL_ENTRY_PC, rc, POSTCODE_SEVERITY_INFO); + + if (x) { + POSTCODE_LINUX_2(MALLOC_FAILURE_PC, POSTCODE_SEVERITY_ERR); + goto away; + } + if (visorchannel_clear(chan, 0, 0, allocated_bytes) < 0) { + POSTCODE_LINUX_2(VBUS_CHANNEL_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + goto away; + } + if (visorchannel_read + (chan, 0, x, sizeof(struct spar_vbus_channel_protocol)) < 0) { + POSTCODE_LINUX_2(VBUS_CHANNEL_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + goto away; + } + if (!SPAR_VBUS_CHANNEL_OK_SERVER(allocated_bytes)) { + POSTCODE_LINUX_2(VBUS_CHANNEL_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + goto away; + } + + if (visorchannel_write + (chan, 0, x, sizeof(struct spar_vbus_channel_protocol)) < 0) { + POSTCODE_LINUX_3(VBUS_CHANNEL_FAILURE_PC, chan, + POSTCODE_SEVERITY_ERR); + goto away; + } + + POSTCODE_LINUX_3(VBUS_CHANNEL_EXIT_PC, chan, POSTCODE_SEVERITY_INFO); + rc = 0; + +away: + kfree(x); + x = NULL; + return rc; +} + +static int +get_vbus_header_info(struct visorchannel *chan, + struct spar_vbus_headerinfo *hdr_info) +{ + int rc = -1; + + if (!SPAR_VBUS_CHANNEL_OK_CLIENT(visorchannel_get_header(chan))) + goto away; + if (visorchannel_read(chan, sizeof(struct channel_header), hdr_info, + sizeof(*hdr_info)) < 0) { + goto away; + } + if (hdr_info->struct_bytes < sizeof(struct spar_vbus_headerinfo)) + goto away; + if (hdr_info->device_info_struct_bytes < + sizeof(struct ultra_vbus_deviceinfo)) { + goto away; + } + rc = 0; +away: + return rc; +} + +/* Write the contents of to the struct + * spar_vbus_channel_protocol.chp_info. */ + +static int +write_vbus_chp_info(struct visorchannel *chan, + struct spar_vbus_headerinfo *hdr_info, + struct ultra_vbus_deviceinfo *info) +{ + int off = sizeof(struct channel_header) + hdr_info->chp_info_offset; + + if (hdr_info->chp_info_offset == 0) + return -1; + + if (visorchannel_write(chan, off, info, sizeof(*info)) < 0) + return -1; + return 0; +} + +/* Write the contents of to the struct + * spar_vbus_channel_protocol.bus_info. */ + +static int +write_vbus_bus_info(struct visorchannel *chan, + struct spar_vbus_headerinfo *hdr_info, + struct ultra_vbus_deviceinfo *info) +{ + int off = sizeof(struct channel_header) + hdr_info->bus_info_offset; + + if (hdr_info->bus_info_offset == 0) + return -1; + + if (visorchannel_write(chan, off, info, sizeof(*info)) < 0) + return -1; + return 0; +} + +/* Write the contents of to the + * struct spar_vbus_channel_protocol.dev_info[]. + */ +static int +write_vbus_dev_info(struct visorchannel *chan, + struct spar_vbus_headerinfo *hdr_info, + struct ultra_vbus_deviceinfo *info, int devix) +{ + int off = + (sizeof(struct channel_header) + hdr_info->dev_info_offset) + + (hdr_info->device_info_struct_bytes * devix); + + if (hdr_info->dev_info_offset == 0) + return -1; + + if (visorchannel_write(chan, off, info, sizeof(*info)) < 0) + return -1; + 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_dev_info(struct visor_device *visordev) +{ + int i; + struct visorchipset_bus_info bus_info; + struct visorbus_devdata *devdata = NULL; + struct visor_driver *visordrv; + int bus_no = visordev->chipset_bus_no; + int dev_no = visordev->chipset_dev_no; + struct ultra_vbus_deviceinfo dev_info; + const char *chan_type_name = NULL; + + if (!visordev->device.driver) + return; + + visordrv = to_visor_driver(visordev->device.driver); + if (!visorchipset_get_bus_info(bus_no, &bus_info)) + return; + + devdata = (struct visorbus_devdata *)(bus_info.bus_driver_context); + if (!devdata) + return; + + if (!devdata->vbus_valid) + return; + + /* Within the list of device types (by GUID) that the driver + * says it supports, find out which one of those types matches + * the type of this device, so that we can include the device + * type name + */ + for (i = 0; visordrv->channel_types[i].name; i++) { + if (STRUCTSEQUAL(visordrv->channel_types[i].guid, + visordev->channel_type_guid)) { + chan_type_name = visordrv->channel_types[i].name; + break; + } + } + + bus_device_info_init(&dev_info, chan_type_name, + visordrv->name, visordrv->version, + visordrv->vertag); + write_vbus_dev_info(devdata->chan, + &devdata->vbus_hdr_info, &dev_info, dev_no); + + /* Re-write bus+chipset info, because it is possible that this + * was previously written by our evil counterpart, virtpci. + */ + write_vbus_chp_info(devdata->chan, &devdata->vbus_hdr_info, + &chipset_driverinfo); + write_vbus_bus_info(devdata->chan, &devdata->vbus_hdr_info, + &clientbus_driverinfo); +} + +/** Create a device instance for the visor bus itself. + */ +static struct visorbus_devdata * +create_bus_instance(int id) +{ + struct visorbus_devdata *rc = NULL; + struct visorbus_devdata *devdata = NULL; + struct device *dev; + struct visorchipset_bus_info bus_info; + + POSTCODE_LINUX_2(BUS_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO); + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + POSTCODE_LINUX_2(MALLOC_FAILURE_PC, POSTCODE_SEVERITY_ERR); + rc = NULL; + goto away; + } + memset(dev, 0, sizeof(struct device)); + dev_set_name(dev, "visorbus%d", id); + dev->release = visorbus_release_busdevice; + if (device_register(dev) < 0) { + POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, id, + POSTCODE_SEVERITY_ERR); + rc = NULL; + goto away; + } + devdata = kmalloc(sizeof(*devdata), GFP_KERNEL); + if (!devdata) { + POSTCODE_LINUX_2(MALLOC_FAILURE_PC, POSTCODE_SEVERITY_ERR); + rc = NULL; + goto away; + } + memset(devdata, 0, sizeof(struct visorbus_devdata)); + devdata->devno = id; + devdata->dev = dev; + if ((visorchipset_get_bus_info(id, &bus_info)) && + (bus_info.chan_info.channel_addr > 0) && + (bus_info.chan_info.n_channel_bytes > 0)) { + HOSTADDRESS channel_addr = bus_info.chan_info.channel_addr; + unsigned long n_channel_bytes = + (unsigned long) + bus_info.chan_info.n_channel_bytes; + uuid_le channel_type_guid = + bus_info.chan_info.channel_type_uuid; + + devdata->chan = visorchannel_create(channel_addr, + n_channel_bytes, + channel_type_guid); + if (!devdata->chan) { + POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, channel_addr, + POSTCODE_SEVERITY_ERR); + } else { + if (bus_info.flags.server) { + init_vbus_channel(devdata->chan); + } else { + if (get_vbus_header_info(devdata->chan, + &devdata-> + vbus_hdr_info) >= 0) { + devdata->vbus_valid = TRUE; + write_vbus_chp_info(devdata->chan, + &devdata-> + vbus_hdr_info, + &chipset_driverinfo + ); + write_vbus_bus_info(devdata->chan, + &devdata-> + vbus_hdr_info, + &clientbus_driverinfo); + } + } + } + } + register_businst_attributes(devdata); + bus_count++; + list_add_tail(&devdata->list_all, &list_all_bus_instances); + if (id == 0) + devdata = devdata; /* for testing ONLY */ + dev_set_drvdata(dev, devdata); + rc = devdata; +away: + return rc; +} + +/** Remove a device instance for the visor bus itself. + */ +static void +remove_bus_instance(struct visorbus_devdata *devdata) +{ + /* Note that this will result in the release method for + * devdata->dev being called, which will call + * visorbus_release_busdevice(). This has something to do with + * the put_device() done in device_unregister(), but I have never + * successfully been able to trace thru the code to see where/how + * release() gets called. But I know it does. + */ + unregister_businst_attributes(devdata); + bus_count--; + if (devdata->chan) { + visorchannel_destroy(devdata->chan); + devdata->chan = NULL; + } + list_del(&devdata->list_all); + device_unregister(devdata->dev); +} + +/** Create and register the one-and-only one instance of + * the visor bus type (visorbus_type). + */ +static int +create_bus_type(void) +{ + int rc = 0; + + visorbus_type.dev_attrs = visor_device_attrs; + rc = bus_register(&visorbus_type); + if (rc < 0) + return rc; + + rc = register_bustype_attributes(); + return rc; +} + +/** Remove the one-and-only one instance of the visor bus type (visorbus_type). + */ +static void +remove_bus_type(void) +{ + unregister_bustype_attributes(); + bus_unregister(&visorbus_type); +} + +/** Remove all child visor bus device instances. + */ +static void +remove_all_visor_devices(void) +{ + struct list_head *listentry, *listtmp; + + list_for_each_safe(listentry, listtmp, &list_all_device_instances) { + struct visor_device *dev = list_entry(listentry, + struct visor_device, + list_all); + remove_visor_device(dev); + } +} + +static bool entered_testing_mode = FALSE; +static struct visorchipset_channel_info test_channel_infos[MAXDEVICETEST]; +static unsigned long test_bus_nos[MAXDEVICETEST]; +static unsigned long test_dev_nos[MAXDEVICETEST]; + +static void +chipset_bus_create(u32 bus_no) +{ + struct visorchipset_bus_info bus_info; + struct visorbus_devdata *devdata; + int rc = -1; + + POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO); + if (!visorchipset_get_bus_info(bus_no, &bus_info)) + goto away; + devdata = create_bus_instance(bus_no); + if (!devdata) + goto away; + if (!visorchipset_set_bus_context(bus_no, devdata)) + goto away; + POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO); + rc = 0; +away: + if (rc < 0) { + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no, + POSTCODE_SEVERITY_ERR); + return; + } + POSTCODE_LINUX_3(CHIPSET_INIT_SUCCESS_PC, bus_no, + POSTCODE_SEVERITY_INFO); + if (chipset_responders.bus_create) + (*chipset_responders.bus_create) (bus_no, rc); +} + +static void +chipset_bus_destroy(u32 bus_no) +{ + struct visorchipset_bus_info bus_info; + struct visorbus_devdata *devdata; + int rc = -1; + + if (!visorchipset_get_bus_info(bus_no, &bus_info)) + goto away; + devdata = (struct visorbus_devdata *)(bus_info.bus_driver_context); + if (!devdata) + goto away; + remove_bus_instance(devdata); + if (!visorchipset_set_bus_context(bus_no, NULL)) + goto away; + rc = 0; +away: + if (rc < 0) + return; + if (chipset_responders.bus_destroy) + (*chipset_responders.bus_destroy)(bus_no, rc); +} + +static void +chipset_device_create(u32 bus_no, u32 dev_no) +{ + struct visorchipset_device_info dev_info; + struct visorchipset_bus_info bus_info; + struct visorbus_devdata *devdata = NULL; + int rc = -1; + + POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no, + POSTCODE_SEVERITY_INFO); + + if (entered_testing_mode) + return; + if (!visorchipset_get_device_info(bus_no, dev_no, &dev_info)) + goto away; + if (!visorchipset_get_bus_info(bus_no, &bus_info)) + goto away; + if (visorbus_devicetest) + if (total_devices_created < MAXDEVICETEST) { + test_channel_infos[total_devices_created] = + dev_info.chan_info; + test_bus_nos[total_devices_created] = bus_no; + test_dev_nos[total_devices_created] = dev_no; + } + POSTCODE_LINUX_4(DEVICE_CREATE_EXIT_PC, dev_no, bus_no, + POSTCODE_SEVERITY_INFO); + rc = 0; +away: + if (rc < 0) { + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, + POSTCODE_SEVERITY_ERR); + return; + } + devdata = (struct visorbus_devdata *)(bus_info.bus_driver_context); + rc = create_visor_device(devdata, bus_no, dev_no, + dev_info.chan_info, bus_info.partition_handle); + POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, dev_no, bus_no, + POSTCODE_SEVERITY_INFO); + if (rc < 0) + if (chipset_responders.device_create) + (*chipset_responders.device_create)(bus_no, dev_no, rc); +} + +static void +chipset_device_destroy(u32 bus_no, u32 dev_no) +{ + struct visorchipset_device_info dev_info; + struct visor_device *dev; + int rc = -1; + + if (entered_testing_mode) + return; + if (!visorchipset_get_device_info(bus_no, dev_no, &dev_info)) + goto away; + dev = find_visor_device_by_channel(dev_info.chan_info.channel_addr); + if (!dev) + goto away; + rc = 0; +away: + if (rc < 0) + return; + + if (chipset_responders.device_destroy) + (*chipset_responders.device_destroy) (bus_no, dev_no, rc); + remove_visor_device(dev); +} + +/* This is the callback function specified for a function driver, to + * be called when a pending "pause device" operation has been + * completed. + */ +static void +pause_state_change_complete(struct visor_device *dev, int status) +{ + if (!dev->pausing) + return; + + dev->pausing = FALSE; + if (!chipset_responders.device_pause) /* this can never happen! */ + return; + + /* Notify the chipset driver that the pause is complete, which + * will presumably want to send some sort of response to the + * initiator. */ + (*chipset_responders.device_pause) (dev->chipset_bus_no, + dev->chipset_dev_no, status); +} + +/* This is the callback function specified for a function driver, to + * be called when a pending "resume device" operation has been + * completed. + */ +static void +resume_state_change_complete(struct visor_device *dev, int status) +{ + if (!dev->resuming) + return; + + dev->resuming = FALSE; + if (!chipset_responders.device_resume) /* this can never happen! */ + return; + + /* Notify the chipset driver that the resume is complete, + * which will presumably want to send some sort of response to + * the initiator. */ + (*chipset_responders.device_resume) (dev->chipset_bus_no, + dev->chipset_dev_no, status); +} + +/* Tell the subordinate function driver for a specific device to pause + * or resume that device. Result is returned asynchronously via a + * callback function. + */ +static void +initiate_chipset_device_pause_resume(u32 bus_no, u32 dev_no, bool is_pause) +{ + struct visorchipset_device_info dev_info; + struct visor_device *dev = NULL; + int rc = -1, x; + struct visor_driver *drv = NULL; + void (*notify_func)(u32 bus_no, u32 dev_no, int response) = NULL; + + if (is_pause) + notify_func = chipset_responders.device_pause; + else + notify_func = chipset_responders.device_resume; + if (!notify_func) + goto away; + + if (!visorchipset_get_device_info(bus_no, dev_no, &dev_info)) + goto away; + + dev = find_visor_device_by_channel(dev_info.chan_info.channel_addr); + if (!dev) + goto away; + + drv = to_visor_driver(dev->device.driver); + if (!drv) + goto away; + + if (dev->pausing || dev->resuming) + goto away; + + /* Note that even though both drv->pause() and drv->resume + * specify a callback function, it is NOT necessary for us to + * increment our local module usage count. Reason is, there + * is already a linkage dependency between child function + * drivers and visorbus, so it is already IMPOSSIBLE to unload + * visorbus while child function drivers are still running. + */ + if (is_pause) { + if (!drv->pause) + goto away; + + dev->pausing = TRUE; + x = drv->pause(dev, pause_state_change_complete); + } else { + /* 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_dev_info(dev); + if (!drv->resume) + goto away; + + dev->resuming = TRUE; + x = drv->resume(dev, resume_state_change_complete); + } + if (x < 0) { + if (is_pause) + dev->pausing = FALSE; + else + dev->resuming = FALSE; + goto away; + } + rc = 0; +away: + if (rc < 0) { + if (notify_func) + (*notify_func)(bus_no, dev_no, rc); + } +} + +static void +chipset_device_pause(u32 bus_no, u32 dev_no) +{ + initiate_chipset_device_pause_resume(bus_no, dev_no, TRUE); +} + +static void +chipset_device_resume(u32 bus_no, u32 dev_no) +{ + initiate_chipset_device_pause_resume(bus_no, dev_no, FALSE); +} + +struct channel_size_info { + uuid_le guid; + unsigned long min_size; + unsigned long max_size; +}; + +static int __init +visorbus_init(void) +{ + int rc = 0; + + POSTCODE_LINUX_3(DRIVER_ENTRY_PC, rc, POSTCODE_SEVERITY_INFO); + bus_device_info_init(&clientbus_driverinfo, + "clientbus", MYDRVNAME, + VERSION, NULL); + + /* process module options */ + + if (visorbus_devicetest > MAXDEVICETEST) + visorbus_devicetest = MAXDEVICETEST; + + rc = create_bus_type(); + if (rc < 0) { + POSTCODE_LINUX_2(BUS_CREATE_ENTRY_PC, DIAG_SEVERITY_ERR); + goto away; + } + + periodic_dev_workqueue = create_singlethread_workqueue("visorbus_dev"); + if (!periodic_dev_workqueue) { + POSTCODE_LINUX_2(CREATE_WORKQUEUE_PC, DIAG_SEVERITY_ERR); + rc = -ENOMEM; + goto away; + } + + /* This enables us to receive notifications when devices appear for + * which this service partition is to be a server for. + */ + visorchipset_register_busdev_server(&chipset_notifiers, + &chipset_responders, + &chipset_driverinfo); + + rc = 0; + +away: + if (rc) + POSTCODE_LINUX_3(CHIPSET_INIT_FAILURE_PC, rc, + POSTCODE_SEVERITY_ERR); + return rc; +} + +static void +visorbus_exit(void) +{ + struct list_head *listentry, *listtmp; + + visorchipset_register_busdev_server(NULL, NULL, NULL); + remove_all_visor_devices(); + + flush_workqueue(periodic_dev_workqueue); /* better not be any work! */ + destroy_workqueue(periodic_dev_workqueue); + periodic_dev_workqueue = NULL; + + if (periodic_test_workqueue) { + cancel_delayed_work(&periodic_work); + flush_workqueue(periodic_test_workqueue); + destroy_workqueue(periodic_test_workqueue); + periodic_test_workqueue = NULL; + } + + list_for_each_safe(listentry, listtmp, &list_all_bus_instances) { + struct visorbus_devdata *devdata = list_entry(listentry, + struct + visorbus_devdata, + list_all); + remove_bus_instance(devdata); + } + remove_bus_type(); +} + +module_param_named(debug, visorbus_debug, int, S_IRUGO); +MODULE_PARM_DESC(visorbus_debug, "1 to debug"); +int visorbus_debug = 0; + +module_param_named(forcematch, visorbus_forcematch, int, S_IRUGO); +MODULE_PARM_DESC(visorbus_forcematch, + "1 to force a successful dev <--> drv match"); +int visorbus_forcematch = 0; + +module_param_named(forcenomatch, visorbus_forcenomatch, int, S_IRUGO); +MODULE_PARM_DESC(visorbus_forcenomatch, + "1 to force an UNsuccessful dev <--> drv match"); +int visorbus_forcenomatch = 0; + +module_param_named(devicetest, visorbus_devicetest, int, S_IRUGO); +MODULE_PARM_DESC(visorbus_devicetest, + "non-0 to just test device creation and destruction"); +int visorbus_devicetest = 0; + +module_param_named(debugref, visorbus_debugref, int, S_IRUGO); +MODULE_PARM_DESC(visorbus_debugref, "1 to debug reference counting"); +int visorbus_debugref = 0; + +module_param_named(serialloopbacktest, visorbus_serialloopbacktest, + int, S_IRUGO); +MODULE_PARM_DESC(visorbus_serialloopbacktest, + "non-0 to just create 2 serial devices on the same channel"); +int visorbus_serialloopbacktest = 0; + +module_init(visorbus_init); +module_exit(visorbus_exit); + +MODULE_AUTHOR("Unisys"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Supervisor bus driver for service partition: ver " VERSION); +MODULE_VERSION(VERSION); diff --git a/drivers/staging/unisys/visorbus/visorbus_private.h b/drivers/staging/unisys/visorbus/visorbus_private.h new file mode 100644 index 000000000000..2b61312789da --- /dev/null +++ b/drivers/staging/unisys/visorbus/visorbus_private.h @@ -0,0 +1,50 @@ +/* visorbus_private.h + * + * Copyright (C) 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __VISORBUS_PRIVATE_H__ +#define __VISORBUS_PRIVATE_H__ + +#include "timskmod.h" +#include "visorbus.h" +#include "visorchipset.h" +#include "visorchannel.h" +#include "version.h" +#include "vbuschannel.h" + +/* module parameters */ +extern int visorbus_debug; +extern int visorbus_forcematch; +extern int visorbus_forcenomatch; +#define MAXDEVICETEST 4 +extern int visorbus_devicetest; +extern int visorbus_debugref; +extern int visorbus_serialloopbacktest; +#define SERIALLOOPBACKCHANADDR (100 * 1024 * 1024) + +/** This is the private data that we store for each bus device instance. + */ +struct visorbus_devdata { + int devno; /* this is the chipset busNo */ + struct list_head list_all; + struct device *dev; + struct kobject kobj; + struct visorchannel *chan; /* channel area for bus itself */ + bool vbus_valid; + struct spar_vbus_headerinfo vbus_hdr_info; +}; + +#endif -- 2.34.1