diff options
Diffstat (limited to 'drivers/staging/unisys/visorbus')
-rw-r--r-- | drivers/staging/unisys/visorbus/Kconfig | 10 | ||||
-rw-r--r-- | drivers/staging/unisys/visorbus/Makefile | 14 | ||||
-rw-r--r-- | drivers/staging/unisys/visorbus/businst_attr.c | 103 | ||||
-rw-r--r-- | drivers/staging/unisys/visorbus/businst_attr.h | 40 | ||||
-rw-r--r-- | drivers/staging/unisys/visorbus/channel_attr.c | 227 | ||||
-rw-r--r-- | drivers/staging/unisys/visorbus/channel_attr.h | 27 | ||||
-rw-r--r-- | drivers/staging/unisys/visorbus/devmajorminor_attr.c | 192 | ||||
-rw-r--r-- | drivers/staging/unisys/visorbus/devmajorminor_attr.h | 31 | ||||
-rw-r--r-- | drivers/staging/unisys/visorbus/visorbus.h | 166 | ||||
-rw-r--r-- | drivers/staging/unisys/visorbus/visorbus_main.c | 1643 | ||||
-rw-r--r-- | drivers/staging/unisys/visorbus/visorbus_private.h | 50 |
11 files changed, 2503 insertions, 0 deletions
diff --git a/drivers/staging/unisys/visorbus/Kconfig b/drivers/staging/unisys/visorbus/Kconfig new file mode 100644 index 0000000..0141528 --- /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 0000000..60bb96b --- /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 0000000..b3fea9d --- /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/<bustypename>/ + * version + * devices + * <devname1> --> /sys/devices/<businstancename><devname1> + * <devname2> --> /sys/devices/<businstancename><devname2> + * drivers + * <driverinstancename1> + * <driverinstance1property1> + * <driverinstance1property2> + * ... + * <driverinstancename2> + * <driverinstance2property1> + * <driverinstance2property2> + * ... + * >> busses + * >> <businstancename1> + * >> <businstance1property1> + * >> <businstance1property2> + * >> ... + * >> <businstancename2> + * >> <businstance2property1> + * >> <businstance2property2> + * >> ... + * + * I considered adding bus instance properties under + * /sys/devices/<businstancename>. But I thought there may be existing + * notions that ONLY device sub-trees should live under + * /sys/devices/<businstancename>. 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 0000000..e9fb36a --- /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 0000000..0d7184a --- /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<x>/dev<y>/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<x>/dev<y>/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 0000000..00ae37c --- /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 0000000..ceb4bb7 --- /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<x>/dev<y>/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 0000000..0b55cb1 --- /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 0000000..856fa3f --- /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 <linux/device.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/uuid.h> + +#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<x>/dev<y>/channel/ */ + struct kobject kobjdevmajorminor; /* visorbus<x>/dev<y>/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 0000000..d717e35 --- /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 <linux/uuid.h> + +#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<n>. + * + * 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/<drivername>. + * + */ + +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/<devicename>. + * + */ + +#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 <info> 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 <info> 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 <info> to the + * struct spar_vbus_channel_protocol.dev_info[<devix>]. + */ +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 0000000..2b61312 --- /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 |