diff options
author | Erik Arfvidson <erik.arfvidson@unisys.com> | 2015-05-05 22:36:00 (GMT) |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-05-08 13:25:57 (GMT) |
commit | 3703987cd427ca4ca1c7e98308be7f3036007a37 (patch) | |
tree | 24996eee09c2e78383a1042394e32506f7f25567 /drivers/staging/unisys/visorbus/visorbus_main.c | |
parent | 7e61e4c9b34c51913997830682181a24febe810d (diff) | |
download | linux-3703987cd427ca4ca1c7e98308be7f3036007a37.tar.xz |
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 <erik.arfvidson@unisys.com>
Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/unisys/visorbus/visorbus_main.c')
-rw-r--r-- | drivers/staging/unisys/visorbus/visorbus_main.c | 1643 |
1 files changed, 1643 insertions, 0 deletions
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); |