2015-05-05 16:36:00 -06:00
|
|
|
|
/* visorbus_main.c
|
|
|
|
|
*
|
2015-07-16 10:40:48 -06:00
|
|
|
|
* Copyright <EFBFBD> 2010 - 2015 UNISYS CORPORATION
|
2015-05-05 16:36:00 -06:00
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
2015-07-16 10:40:48 -06:00
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
2015-05-05 16:36:00 -06:00
|
|
|
|
*
|
|
|
|
|
* 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>
|
|
|
|
|
|
2015-05-05 16:36:10 -06:00
|
|
|
|
#include "visorbus.h"
|
2015-05-05 16:36:15 -06:00
|
|
|
|
#include "visorbus_private.h"
|
2015-05-05 16:36:10 -06:00
|
|
|
|
#include "version.h"
|
2015-05-05 16:36:00 -06:00
|
|
|
|
#include "periodic_work.h"
|
|
|
|
|
#include "vbuschannel.h"
|
|
|
|
|
#include "guestlinuxdebug.h"
|
2015-05-13 11:22:00 -06:00
|
|
|
|
#include "vmcallinterface.h"
|
2015-05-05 16:36:10 -06:00
|
|
|
|
|
2015-05-05 16:36:15 -06:00
|
|
|
|
#define MYDRVNAME "visorbus"
|
|
|
|
|
|
2015-05-05 16:36:10 -06:00
|
|
|
|
/* module parameters */
|
2015-05-28 20:04:23 -06:00
|
|
|
|
static int visorbus_debug;
|
|
|
|
|
static int visorbus_forcematch;
|
|
|
|
|
static int visorbus_forcenomatch;
|
|
|
|
|
static int visorbus_debugref;
|
2015-05-05 16:36:10 -06:00
|
|
|
|
#define SERIALLOOPBACKCHANADDR (100 * 1024 * 1024)
|
|
|
|
|
|
2016-04-13 10:07:07 -06:00
|
|
|
|
/* Display string that is guaranteed to be no longer the 99 characters*/
|
|
|
|
|
#define LINESIZE 99
|
|
|
|
|
|
2015-05-05 16:36:00 -06:00
|
|
|
|
#define CURRENT_FILE_PC VISOR_BUS_PC_visorbus_main_c
|
|
|
|
|
#define POLLJIFFIES_TESTWORK 100
|
|
|
|
|
#define POLLJIFFIES_NORMALCHANNEL 10
|
|
|
|
|
|
2015-09-04 10:01:32 -06:00
|
|
|
|
static int busreg_rc = -ENODEV; /* stores the result from bus registration */
|
|
|
|
|
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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);
|
|
|
|
|
|
2015-05-13 11:22:15 -06:00
|
|
|
|
/* BUS type attributes
|
|
|
|
|
*
|
|
|
|
|
* define & implement display of bus attributes under
|
|
|
|
|
* /sys/bus/visorbus.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static ssize_t version_show(struct bus_type *bus, char *buf)
|
|
|
|
|
{
|
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", VERSION);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BUS_ATTR_RO(version);
|
|
|
|
|
|
|
|
|
|
static struct attribute *visorbus_bus_attrs[] = {
|
|
|
|
|
&bus_attr_version.attr,
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct attribute_group visorbus_bus_group = {
|
|
|
|
|
.attrs = visorbus_bus_attrs,
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-28 20:04:23 -06:00
|
|
|
|
static const struct attribute_group *visorbus_bus_groups[] = {
|
2015-05-13 11:22:15 -06:00
|
|
|
|
&visorbus_bus_group,
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
2015-07-24 10:06:53 -06:00
|
|
|
|
/*
|
|
|
|
|
* DEVICE type attributes
|
|
|
|
|
*
|
|
|
|
|
* The modalias file will contain the guid of the device.
|
|
|
|
|
*/
|
|
|
|
|
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
|
char *buf)
|
|
|
|
|
{
|
|
|
|
|
struct visor_device *vdev;
|
|
|
|
|
uuid_le guid;
|
|
|
|
|
|
|
|
|
|
vdev = to_visor_device(dev);
|
|
|
|
|
guid = visorchannel_get_uuid(vdev->visorchannel);
|
|
|
|
|
return snprintf(buf, PAGE_SIZE, "visorbus:%pUl\n", &guid);
|
|
|
|
|
}
|
|
|
|
|
static DEVICE_ATTR_RO(modalias);
|
|
|
|
|
|
|
|
|
|
static struct attribute *visorbus_dev_attrs[] = {
|
|
|
|
|
&dev_attr_modalias.attr,
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* sysfs example for bridge-only sysfs files using device_type's */
|
|
|
|
|
static const struct attribute_group visorbus_dev_group = {
|
|
|
|
|
.attrs = visorbus_dev_attrs,
|
|
|
|
|
};
|
|
|
|
|
|
2015-08-11 06:07:00 -06:00
|
|
|
|
static const struct attribute_group *visorbus_dev_groups[] = {
|
2015-07-24 10:06:53 -06:00
|
|
|
|
&visorbus_dev_group,
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-05 16:36:00 -06:00
|
|
|
|
/** This describes the TYPE of bus.
|
|
|
|
|
* (Don't confuse this with an INSTANCE of the bus.)
|
|
|
|
|
*/
|
2015-05-13 11:22:18 -06:00
|
|
|
|
struct bus_type visorbus_type = {
|
2015-05-05 16:36:00 -06:00
|
|
|
|
.name = "visorbus",
|
|
|
|
|
.match = visorbus_match,
|
|
|
|
|
.uevent = visorbus_uevent,
|
2015-07-24 10:06:53 -06:00
|
|
|
|
.dev_groups = visorbus_dev_groups,
|
2015-05-13 11:22:15 -06:00
|
|
|
|
.bus_groups = visorbus_bus_groups,
|
2015-05-05 16:36:00 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 */
|
|
|
|
|
/** ever-increasing */
|
|
|
|
|
|
2015-06-04 07:22:41 -06:00
|
|
|
|
static void chipset_bus_create(struct visor_device *bus_info);
|
|
|
|
|
static void chipset_bus_destroy(struct visor_device *bus_info);
|
2015-06-04 07:22:42 -06:00
|
|
|
|
static void chipset_device_create(struct visor_device *dev_info);
|
|
|
|
|
static void chipset_device_destroy(struct visor_device *dev_info);
|
|
|
|
|
static void chipset_device_pause(struct visor_device *dev_info);
|
|
|
|
|
static void chipset_device_resume(struct visor_device *dev_info);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
/** 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;
|
|
|
|
|
|
2015-06-04 07:22:37 -06:00
|
|
|
|
/** list of visor_device structs, linked via .list_all */
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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)
|
|
|
|
|
{
|
2015-07-24 10:06:53 -06:00
|
|
|
|
struct visor_device *dev;
|
|
|
|
|
uuid_le guid;
|
|
|
|
|
|
|
|
|
|
dev = to_visor_device(xdev);
|
|
|
|
|
guid = visorchannel_get_uuid(dev->visorchannel);
|
|
|
|
|
|
|
|
|
|
if (add_uevent_var(env, "MODALIAS=visorbus:%pUl", &guid))
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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 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);
|
|
|
|
|
|
2016-03-12 19:27:11 -07:00
|
|
|
|
if (visorbus_forcematch)
|
|
|
|
|
return 1;
|
|
|
|
|
if (visorbus_forcenomatch)
|
|
|
|
|
return 0;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
if (!drv->channel_types)
|
2016-03-12 19:27:11 -07:00
|
|
|
|
return 0;
|
|
|
|
|
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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,
|
2016-03-12 19:27:11 -07:00
|
|
|
|
channel_type) == 0)
|
|
|
|
|
return i + 1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 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)
|
|
|
|
|
{
|
2015-06-04 07:22:37 -06:00
|
|
|
|
struct visor_device *dev = dev_get_drvdata(xdev);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-06-04 07:22:37 -06:00
|
|
|
|
kfree(dev);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 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);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-05 16:36:06 -06:00
|
|
|
|
/* begin implementation of specific channel attributes to appear under
|
|
|
|
|
* /sys/bus/visorbus<x>/dev<y>/channel
|
|
|
|
|
*/
|
2015-05-13 11:22:17 -06:00
|
|
|
|
static ssize_t physaddr_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
|
char *buf)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
{
|
2015-05-13 11:22:17 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
|
|
|
|
|
|
|
|
|
if (!vdev->visorchannel)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
return 0;
|
2016-05-06 11:11:18 -06:00
|
|
|
|
return snprintf(buf, PAGE_SIZE, "0x%llx\n",
|
2015-05-13 11:22:17 -06:00
|
|
|
|
visorchannel_get_physaddr(vdev->visorchannel));
|
2015-05-05 16:36:06 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
static ssize_t nbytes_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
|
char *buf)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
{
|
2015-05-13 11:22:17 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
|
|
|
|
|
|
|
|
|
if (!vdev->visorchannel)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
return 0;
|
|
|
|
|
return snprintf(buf, PAGE_SIZE, "0x%lx\n",
|
2015-05-13 11:22:17 -06:00
|
|
|
|
visorchannel_get_nbytes(vdev->visorchannel));
|
2015-05-05 16:36:06 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
static ssize_t clientpartition_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
|
{
|
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
|
|
|
|
|
|
|
|
|
if (!vdev->visorchannel)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
return 0;
|
2016-05-06 11:11:18 -06:00
|
|
|
|
return snprintf(buf, PAGE_SIZE, "0x%llx\n",
|
2015-05-13 11:22:17 -06:00
|
|
|
|
visorchannel_get_clientpartition(vdev->visorchannel));
|
2015-05-05 16:36:06 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
static ssize_t typeguid_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
|
char *buf)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
{
|
2015-05-13 11:22:17 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
2016-04-13 10:07:07 -06:00
|
|
|
|
char typeid[LINESIZE];
|
2015-05-05 16:36:06 -06:00
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
if (!vdev->visorchannel)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
return 0;
|
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n",
|
2016-03-23 20:15:49 -06:00
|
|
|
|
visorchannel_id(vdev->visorchannel, typeid));
|
2015-05-05 16:36:06 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
static ssize_t zoneguid_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
|
char *buf)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
{
|
2015-05-13 11:22:17 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
2016-04-13 10:07:07 -06:00
|
|
|
|
char zoneid[LINESIZE];
|
2015-05-05 16:36:06 -06:00
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
if (!vdev->visorchannel)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
return 0;
|
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n",
|
2016-03-23 20:15:50 -06:00
|
|
|
|
visorchannel_zoneid(vdev->visorchannel, zoneid));
|
2015-05-05 16:36:06 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
static ssize_t typename_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
|
char *buf)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
{
|
2015-05-13 11:22:17 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
2015-05-05 16:36:06 -06:00
|
|
|
|
int i = 0;
|
2015-05-13 11:22:17 -06:00
|
|
|
|
struct bus_type *xbus = dev->bus;
|
|
|
|
|
struct device_driver *xdrv = dev->driver;
|
2015-05-05 16:36:06 -06:00
|
|
|
|
struct visor_driver *drv = NULL;
|
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
if (!vdev->visorchannel || !xbus || !xdrv)
|
2015-05-05 16:36:06 -06:00
|
|
|
|
return 0;
|
2015-05-13 11:22:17 -06:00
|
|
|
|
i = xbus->match(dev, xdrv);
|
2015-05-05 16:36:06 -06:00
|
|
|
|
if (!i)
|
|
|
|
|
return 0;
|
|
|
|
|
drv = to_visor_driver(xdrv);
|
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", drv->channel_types[i - 1].name);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
static DEVICE_ATTR_RO(physaddr);
|
|
|
|
|
static DEVICE_ATTR_RO(nbytes);
|
|
|
|
|
static DEVICE_ATTR_RO(clientpartition);
|
|
|
|
|
static DEVICE_ATTR_RO(typeguid);
|
|
|
|
|
static DEVICE_ATTR_RO(zoneguid);
|
|
|
|
|
static DEVICE_ATTR_RO(typename);
|
|
|
|
|
|
|
|
|
|
static struct attribute *channel_attrs[] = {
|
|
|
|
|
&dev_attr_physaddr.attr,
|
|
|
|
|
&dev_attr_nbytes.attr,
|
|
|
|
|
&dev_attr_clientpartition.attr,
|
|
|
|
|
&dev_attr_typeguid.attr,
|
|
|
|
|
&dev_attr_zoneguid.attr,
|
|
|
|
|
&dev_attr_typename.attr,
|
2015-07-13 12:51:23 -06:00
|
|
|
|
NULL
|
2015-05-05 16:36:06 -06:00
|
|
|
|
};
|
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
static struct attribute_group channel_attr_grp = {
|
|
|
|
|
.name = "channel",
|
|
|
|
|
.attrs = channel_attrs,
|
2015-05-05 16:36:06 -06:00
|
|
|
|
};
|
|
|
|
|
|
2015-07-24 10:06:53 -06:00
|
|
|
|
static const struct attribute_group *visorbus_channel_groups[] = {
|
2015-05-13 11:22:17 -06:00
|
|
|
|
&channel_attr_grp,
|
|
|
|
|
NULL
|
2015-05-05 16:36:06 -06:00
|
|
|
|
};
|
|
|
|
|
|
2015-05-13 11:22:17 -06:00
|
|
|
|
/* end implementation of specific channel attributes */
|
2015-05-05 16:36:06 -06:00
|
|
|
|
|
2015-05-05 16:36:00 -06:00
|
|
|
|
/* 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.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
2015-05-13 11:22:13 -06:00
|
|
|
|
static ssize_t partition_handle_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
char *buf) {
|
2015-05-13 11:22:23 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
|
|
|
|
u64 handle = visorchannel_get_clientpartition(vdev->visorchannel);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2016-05-06 11:11:18 -06:00
|
|
|
|
return snprintf(buf, PAGE_SIZE, "0x%llx\n", handle);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:13 -06:00
|
|
|
|
static ssize_t partition_guid_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
char *buf) {
|
2015-05-13 11:22:23 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-05-13 11:22:23 -06:00
|
|
|
|
return snprintf(buf, PAGE_SIZE, "{%pUb}\n", &vdev->partition_uuid);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:13 -06:00
|
|
|
|
static ssize_t partition_name_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
char *buf) {
|
2015-05-13 11:22:23 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-05-13 11:22:23 -06:00
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", vdev->name);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:13 -06:00
|
|
|
|
static ssize_t channel_addr_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
char *buf) {
|
2015-05-13 11:22:23 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
|
|
|
|
u64 addr = visorchannel_get_physaddr(vdev->visorchannel);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2016-05-06 11:11:18 -06:00
|
|
|
|
return snprintf(buf, PAGE_SIZE, "0x%llx\n", addr);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:13 -06:00
|
|
|
|
static ssize_t channel_bytes_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
char *buf) {
|
2015-05-13 11:22:23 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
|
|
|
|
u64 nbytes = visorchannel_get_nbytes(vdev->visorchannel);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2016-05-06 11:11:18 -06:00
|
|
|
|
return snprintf(buf, PAGE_SIZE, "0x%llx\n", nbytes);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:13 -06:00
|
|
|
|
static ssize_t channel_id_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
char *buf) {
|
2015-05-13 11:22:23 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
int len = 0;
|
|
|
|
|
|
2015-05-13 11:22:23 -06:00
|
|
|
|
if (vdev->visorchannel) {
|
|
|
|
|
visorchannel_id(vdev->visorchannel, buf);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
len = strlen(buf);
|
|
|
|
|
buf[len++] = '\n';
|
|
|
|
|
}
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:13 -06:00
|
|
|
|
static ssize_t client_bus_info_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
char *buf) {
|
2015-05-13 11:22:23 -06:00
|
|
|
|
struct visor_device *vdev = to_visor_device(dev);
|
|
|
|
|
struct visorchannel *channel = vdev->visorchannel;
|
|
|
|
|
|
2016-03-23 20:15:52 -06:00
|
|
|
|
int i, shift, remain = PAGE_SIZE;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
unsigned long off;
|
2016-03-23 20:15:51 -06:00
|
|
|
|
char *pos = buf;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
u8 *partition_name;
|
|
|
|
|
struct ultra_vbus_deviceinfo dev_info;
|
|
|
|
|
|
|
|
|
|
partition_name = "";
|
2015-05-13 11:22:23 -06:00
|
|
|
|
if (channel) {
|
|
|
|
|
if (vdev->name)
|
|
|
|
|
partition_name = vdev->name;
|
2016-03-23 20:15:52 -06:00
|
|
|
|
shift = snprintf(pos, remain,
|
|
|
|
|
"Client device / client driver info for %s eartition (vbus #%d):\n",
|
|
|
|
|
partition_name, vdev->chipset_dev_no);
|
|
|
|
|
pos += shift;
|
|
|
|
|
remain -= shift;
|
|
|
|
|
shift = visorchannel_read(channel,
|
|
|
|
|
offsetof(struct
|
|
|
|
|
spar_vbus_channel_protocol,
|
|
|
|
|
chp_info),
|
|
|
|
|
&dev_info, sizeof(dev_info));
|
|
|
|
|
if (shift >= 0) {
|
|
|
|
|
shift = vbuschannel_devinfo_to_string(&dev_info, pos,
|
|
|
|
|
remain, -1);
|
|
|
|
|
pos += shift;
|
|
|
|
|
remain -= shift;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
2016-03-23 20:15:52 -06:00
|
|
|
|
shift = visorchannel_read(channel,
|
|
|
|
|
offsetof(struct
|
|
|
|
|
spar_vbus_channel_protocol,
|
|
|
|
|
bus_info),
|
|
|
|
|
&dev_info, sizeof(dev_info));
|
|
|
|
|
if (shift >= 0) {
|
|
|
|
|
shift = vbuschannel_devinfo_to_string(&dev_info, pos,
|
|
|
|
|
remain, -1);
|
|
|
|
|
pos += shift;
|
|
|
|
|
remain -= shift;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
off = offsetof(struct spar_vbus_channel_protocol, dev_info);
|
|
|
|
|
i = 0;
|
|
|
|
|
while (off + sizeof(dev_info) <=
|
2015-05-13 11:22:23 -06:00
|
|
|
|
visorchannel_get_nbytes(channel)) {
|
2016-03-23 20:15:52 -06:00
|
|
|
|
shift = visorchannel_read(channel,
|
|
|
|
|
off, &dev_info,
|
|
|
|
|
sizeof(dev_info));
|
|
|
|
|
if (shift >= 0) {
|
|
|
|
|
shift = vbuschannel_devinfo_to_string
|
2016-03-23 20:15:51 -06:00
|
|
|
|
(&dev_info, pos, remain, i);
|
2016-03-23 20:15:52 -06:00
|
|
|
|
pos += shift;
|
|
|
|
|
remain -= shift;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
off += sizeof(dev_info);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return PAGE_SIZE - remain;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 11:22:13 -06:00
|
|
|
|
static DEVICE_ATTR_RO(partition_handle);
|
|
|
|
|
static DEVICE_ATTR_RO(partition_guid);
|
|
|
|
|
static DEVICE_ATTR_RO(partition_name);
|
|
|
|
|
static DEVICE_ATTR_RO(channel_addr);
|
|
|
|
|
static DEVICE_ATTR_RO(channel_bytes);
|
|
|
|
|
static DEVICE_ATTR_RO(channel_id);
|
|
|
|
|
static DEVICE_ATTR_RO(client_bus_info);
|
|
|
|
|
|
|
|
|
|
static struct attribute *dev_attrs[] = {
|
|
|
|
|
&dev_attr_partition_handle.attr,
|
|
|
|
|
&dev_attr_partition_guid.attr,
|
|
|
|
|
&dev_attr_partition_name.attr,
|
|
|
|
|
&dev_attr_channel_addr.attr,
|
|
|
|
|
&dev_attr_channel_bytes.attr,
|
|
|
|
|
&dev_attr_channel_id.attr,
|
|
|
|
|
&dev_attr_client_bus_info.attr,
|
|
|
|
|
NULL
|
|
|
|
|
};
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-05-13 11:22:13 -06:00
|
|
|
|
static struct attribute_group dev_attr_grp = {
|
|
|
|
|
.attrs = dev_attrs,
|
|
|
|
|
};
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-05-13 11:22:13 -06:00
|
|
|
|
static const struct attribute_group *visorbus_groups[] = {
|
|
|
|
|
&dev_attr_grp,
|
|
|
|
|
NULL
|
|
|
|
|
};
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
|
|
|
|
struct driver_attribute version =
|
|
|
|
|
__ATTR(version, S_IRUGO, DRIVER_ATTR_version, NULL);
|
|
|
|
|
drv->version_attr = version;
|
2016-02-25 04:56:55 -07:00
|
|
|
|
return driver_create_file(&drv->driver, &drv->version_attr);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
unregister_driver_attributes(struct visor_driver *drv)
|
|
|
|
|
{
|
|
|
|
|
driver_remove_file(&drv->driver, &drv->version_attr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
dev_periodic_work(void *xdev)
|
|
|
|
|
{
|
2015-08-10 02:00:35 -06:00
|
|
|
|
struct visor_device *dev = xdev;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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)
|
|
|
|
|
{
|
2016-03-12 19:27:08 -07:00
|
|
|
|
int res;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
struct visor_driver *drv;
|
|
|
|
|
struct visor_device *dev;
|
|
|
|
|
|
|
|
|
|
drv = to_visor_driver(xdev->driver);
|
|
|
|
|
dev = to_visor_device(xdev);
|
2016-03-12 19:27:08 -07:00
|
|
|
|
|
|
|
|
|
if (!drv->probe)
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
2015-05-05 16:36:00 -06:00
|
|
|
|
down(&dev->visordriver_callback_lock);
|
2015-05-05 16:37:01 -06:00
|
|
|
|
dev->being_removed = false;
|
2016-03-12 19:27:08 -07:00
|
|
|
|
|
|
|
|
|
res = drv->probe(dev);
|
|
|
|
|
if (res >= 0) {
|
|
|
|
|
/* success: reference kept via unmatched get_device() */
|
|
|
|
|
get_device(&dev->device);
|
|
|
|
|
fix_vbus_dev_info(dev);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
up(&dev->visordriver_callback_lock);
|
2016-03-12 19:27:08 -07:00
|
|
|
|
return res;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 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)
|
|
|
|
|
{
|
|
|
|
|
struct visor_device *dev;
|
|
|
|
|
struct visor_driver *drv;
|
|
|
|
|
|
|
|
|
|
dev = to_visor_device(xdev);
|
|
|
|
|
drv = to_visor_driver(xdev->driver);
|
|
|
|
|
down(&dev->visordriver_callback_lock);
|
2015-05-05 16:37:01 -06:00
|
|
|
|
dev->being_removed = true;
|
2016-03-30 18:38:49 -06:00
|
|
|
|
if (drv->remove)
|
|
|
|
|
drv->remove(dev);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
up(&dev->visordriver_callback_lock);
|
|
|
|
|
dev_stop_periodic_work(dev);
|
|
|
|
|
|
|
|
|
|
put_device(&dev->device);
|
2015-06-11 04:03:49 -06:00
|
|
|
|
return 0;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 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;
|
|
|
|
|
|
2015-09-04 10:01:32 -06:00
|
|
|
|
if (busreg_rc < 0)
|
|
|
|
|
return -ENODEV; /*can't register on a nonexistent bus*/
|
|
|
|
|
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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);
|
2015-09-04 10:01:31 -06:00
|
|
|
|
if (rc < 0)
|
|
|
|
|
driver_unregister(&drv->driver);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
/** 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
|
2015-06-04 07:22:42 -06:00
|
|
|
|
create_visor_device(struct visor_device *dev)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
2016-03-12 19:27:12 -07:00
|
|
|
|
int err;
|
2015-06-04 07:22:42 -06:00
|
|
|
|
u32 chipset_bus_no = dev->chipset_bus_no;
|
|
|
|
|
u32 chipset_dev_no = dev->chipset_dev_no;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, chipset_dev_no, chipset_bus_no,
|
|
|
|
|
POSTCODE_SEVERITY_INFO);
|
|
|
|
|
|
|
|
|
|
sema_init(&dev->visordriver_callback_lock, 1); /* unlocked */
|
|
|
|
|
dev->device.bus = &visorbus_type;
|
2015-07-24 10:06:53 -06:00
|
|
|
|
dev->device.groups = visorbus_channel_groups;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
device_initialize(&dev->device);
|
|
|
|
|
dev->device.release = visorbus_release_device;
|
|
|
|
|
/* keep a reference just for us (now 2) */
|
|
|
|
|
get_device(&dev->device);
|
|
|
|
|
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);
|
2016-03-12 19:27:12 -07:00
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto err_put;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
|
*/
|
2015-05-13 11:22:25 -06:00
|
|
|
|
dev_set_name(&dev->device, "vbus%u:dev%u",
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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).
|
|
|
|
|
*/
|
2016-03-12 19:27:12 -07:00
|
|
|
|
err = device_add(&dev->device);
|
|
|
|
|
if (err < 0) {
|
2015-05-05 16:36:00 -06:00
|
|
|
|
POSTCODE_LINUX_3(DEVICE_ADD_PC, chipset_bus_no,
|
|
|
|
|
DIAG_SEVERITY_ERR);
|
2016-03-12 19:27:12 -07:00
|
|
|
|
goto err_put;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-04 07:22:42 -06:00
|
|
|
|
list_add_tail(&dev->list_all, &list_all_device_instances);
|
2016-03-12 19:27:12 -07:00
|
|
|
|
return 0; /* success: reference kept via unmatched get_device() */
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2016-03-12 19:27:12 -07:00
|
|
|
|
err_put:
|
2015-06-04 07:22:42 -06:00
|
|
|
|
put_device(&dev->device);
|
2016-03-12 19:27:12 -07:00
|
|
|
|
return err;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
remove_visor_device(struct visor_device *dev)
|
|
|
|
|
{
|
|
|
|
|
list_del(&dev->list_all);
|
|
|
|
|
put_device(&dev->device);
|
|
|
|
|
device_unregister(&dev->device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
get_vbus_header_info(struct visorchannel *chan,
|
|
|
|
|
struct spar_vbus_headerinfo *hdr_info)
|
|
|
|
|
{
|
|
|
|
|
if (!SPAR_VBUS_CHANNEL_OK_CLIENT(visorchannel_get_header(chan)))
|
2016-02-23 08:01:50 -07:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
2015-05-05 16:36:00 -06:00
|
|
|
|
if (visorchannel_read(chan, sizeof(struct channel_header), hdr_info,
|
|
|
|
|
sizeof(*hdr_info)) < 0) {
|
2016-02-23 08:01:50 -07:00
|
|
|
|
return -EIO;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
if (hdr_info->struct_bytes < sizeof(struct spar_vbus_headerinfo))
|
2016-02-23 08:01:50 -07:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
2015-05-05 16:36:00 -06:00
|
|
|
|
if (hdr_info->device_info_struct_bytes <
|
|
|
|
|
sizeof(struct ultra_vbus_deviceinfo)) {
|
2016-02-23 08:01:50 -07:00
|
|
|
|
return -EINVAL;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
2016-02-23 08:01:50 -07:00
|
|
|
|
return 0;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write the contents of <info> to the struct
|
2015-11-09 21:12:00 -07:00
|
|
|
|
* spar_vbus_channel_protocol.chp_info.
|
|
|
|
|
*/
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
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)
|
2016-05-13 21:17:19 -06:00
|
|
|
|
return -EFAULT;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
if (visorchannel_write(chan, off, info, sizeof(*info)) < 0)
|
2016-05-13 21:17:19 -06:00
|
|
|
|
return -EFAULT;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write the contents of <info> to the struct
|
2015-11-09 21:12:00 -07:00
|
|
|
|
* spar_vbus_channel_protocol.bus_info.
|
|
|
|
|
*/
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
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)
|
2016-05-13 21:17:19 -06:00
|
|
|
|
return -EFAULT;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
if (visorchannel_write(chan, off, info, sizeof(*info)) < 0)
|
2016-05-13 21:17:19 -06:00
|
|
|
|
return -EFAULT;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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)
|
2016-05-13 21:17:19 -06:00
|
|
|
|
return -EFAULT;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
if (visorchannel_write(chan, off, info, sizeof(*info)) < 0)
|
2016-05-13 21:17:19 -06:00
|
|
|
|
return -EFAULT;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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;
|
2015-06-04 07:22:41 -06:00
|
|
|
|
struct visor_device *bdev;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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;
|
2015-06-01 11:00:28 -06:00
|
|
|
|
struct spar_vbus_headerinfo *hdr_info;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
if (!visordev->device.driver)
|
2015-07-13 12:52:26 -06:00
|
|
|
|
return;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-06-01 11:00:28 -06:00
|
|
|
|
hdr_info = (struct spar_vbus_headerinfo *)visordev->vbus_hdr_info;
|
2015-06-04 07:22:41 -06:00
|
|
|
|
if (!hdr_info)
|
|
|
|
|
return;
|
2015-06-01 11:00:28 -06:00
|
|
|
|
|
2015-06-04 07:22:41 -06:00
|
|
|
|
bdev = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL);
|
|
|
|
|
if (!bdev)
|
|
|
|
|
return;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-06-04 07:22:41 -06:00
|
|
|
|
visordrv = to_visor_driver(visordev->device.driver);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
/* 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++) {
|
2015-05-05 16:37:04 -06:00
|
|
|
|
if (memcmp(&visordrv->channel_types[i].guid,
|
|
|
|
|
&visordev->channel_type_guid,
|
|
|
|
|
sizeof(visordrv->channel_types[i].guid)) == 0) {
|
2015-05-05 16:36:00 -06:00
|
|
|
|
chan_type_name = visordrv->channel_types[i].name;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bus_device_info_init(&dev_info, chan_type_name,
|
|
|
|
|
visordrv->name, visordrv->version,
|
|
|
|
|
visordrv->vertag);
|
2015-06-04 07:22:41 -06:00
|
|
|
|
write_vbus_dev_info(bdev->visorchannel, hdr_info, &dev_info, dev_no);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
/* Re-write bus+chipset info, because it is possible that this
|
|
|
|
|
* was previously written by our evil counterpart, virtpci.
|
|
|
|
|
*/
|
2015-06-04 07:22:41 -06:00
|
|
|
|
write_vbus_chp_info(bdev->visorchannel, hdr_info, &chipset_driverinfo);
|
|
|
|
|
write_vbus_bus_info(bdev->visorchannel, hdr_info,
|
|
|
|
|
&clientbus_driverinfo);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Create a device instance for the visor bus itself.
|
|
|
|
|
*/
|
2015-06-04 07:22:41 -06:00
|
|
|
|
static int
|
|
|
|
|
create_bus_instance(struct visor_device *dev)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
2015-06-04 07:22:41 -06:00
|
|
|
|
int id = dev->chipset_bus_no;
|
2015-06-01 11:00:28 -06:00
|
|
|
|
struct spar_vbus_headerinfo *hdr_info;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
POSTCODE_LINUX_2(BUS_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
|
2015-06-01 11:00:28 -06:00
|
|
|
|
|
|
|
|
|
hdr_info = kzalloc(sizeof(*hdr_info), GFP_KERNEL);
|
2016-02-23 08:01:52 -07:00
|
|
|
|
if (!hdr_info)
|
|
|
|
|
return -ENOMEM;
|
2015-06-01 11:00:28 -06:00
|
|
|
|
|
2015-06-04 07:22:37 -06:00
|
|
|
|
dev_set_name(&dev->device, "visorbus%d", id);
|
|
|
|
|
dev->device.bus = &visorbus_type;
|
|
|
|
|
dev->device.groups = visorbus_groups;
|
|
|
|
|
dev->device.release = visorbus_release_busdevice;
|
2015-06-04 07:22:41 -06:00
|
|
|
|
|
2015-06-04 07:22:37 -06:00
|
|
|
|
if (device_register(&dev->device) < 0) {
|
2015-05-05 16:36:00 -06:00
|
|
|
|
POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, id,
|
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
2016-02-23 08:01:52 -07:00
|
|
|
|
kfree(hdr_info);
|
|
|
|
|
return -ENODEV;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
2015-06-04 07:22:41 -06:00
|
|
|
|
|
2015-06-04 07:22:39 -06:00
|
|
|
|
if (get_vbus_header_info(dev->visorchannel, hdr_info) >= 0) {
|
|
|
|
|
dev->vbus_hdr_info = (void *)hdr_info;
|
|
|
|
|
write_vbus_chp_info(dev->visorchannel, hdr_info,
|
|
|
|
|
&chipset_driverinfo);
|
|
|
|
|
write_vbus_bus_info(dev->visorchannel, hdr_info,
|
|
|
|
|
&clientbus_driverinfo);
|
2015-06-01 11:00:26 -06:00
|
|
|
|
} else {
|
2015-06-04 07:22:39 -06:00
|
|
|
|
kfree(hdr_info);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
bus_count++;
|
2015-06-04 07:22:37 -06:00
|
|
|
|
list_add_tail(&dev->list_all, &list_all_bus_instances);
|
|
|
|
|
dev_set_drvdata(&dev->device, dev);
|
2015-06-04 07:22:41 -06:00
|
|
|
|
return 0;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Remove a device instance for the visor bus itself.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
2015-06-04 07:22:37 -06:00
|
|
|
|
remove_bus_instance(struct visor_device *dev)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
|
|
|
|
/* Note that this will result in the release method for
|
2015-06-04 07:22:37 -06:00
|
|
|
|
* dev->dev being called, which will call
|
2015-05-05 16:36:00 -06:00
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
bus_count--;
|
2015-06-04 07:22:37 -06:00
|
|
|
|
if (dev->visorchannel) {
|
|
|
|
|
visorchannel_destroy(dev->visorchannel);
|
|
|
|
|
dev->visorchannel = NULL;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
2015-06-04 07:22:37 -06:00
|
|
|
|
kfree(dev->vbus_hdr_info);
|
|
|
|
|
list_del(&dev->list_all);
|
|
|
|
|
device_unregister(&dev->device);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Create and register the one-and-only one instance of
|
|
|
|
|
* the visor bus type (visorbus_type).
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
create_bus_type(void)
|
|
|
|
|
{
|
2015-09-04 10:01:32 -06:00
|
|
|
|
busreg_rc = bus_register(&visorbus_type);
|
|
|
|
|
return busreg_rc;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Remove the one-and-only one instance of the visor bus type (visorbus_type).
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
remove_bus_type(void)
|
|
|
|
|
{
|
|
|
|
|
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 void
|
2015-06-04 07:22:41 -06:00
|
|
|
|
chipset_bus_create(struct visor_device *dev)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
2015-06-04 07:22:41 -06:00
|
|
|
|
int rc;
|
|
|
|
|
u32 bus_no = dev->chipset_bus_no;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO);
|
2015-06-04 07:22:41 -06:00
|
|
|
|
rc = create_bus_instance(dev);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO);
|
2015-06-04 07:22:41 -06:00
|
|
|
|
|
|
|
|
|
if (rc < 0)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no,
|
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
2015-06-04 07:22:41 -06:00
|
|
|
|
else
|
|
|
|
|
POSTCODE_LINUX_3(CHIPSET_INIT_SUCCESS_PC, bus_no,
|
|
|
|
|
POSTCODE_SEVERITY_INFO);
|
|
|
|
|
|
2015-05-05 16:36:00 -06:00
|
|
|
|
if (chipset_responders.bus_create)
|
2015-06-04 07:22:41 -06:00
|
|
|
|
(*chipset_responders.bus_create) (dev, rc);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2015-06-04 07:22:41 -06:00
|
|
|
|
chipset_bus_destroy(struct visor_device *dev)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
2015-06-04 07:22:37 -06:00
|
|
|
|
remove_bus_instance(dev);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
if (chipset_responders.bus_destroy)
|
2015-06-04 07:22:41 -06:00
|
|
|
|
(*chipset_responders.bus_destroy)(dev, 0);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2015-06-04 07:22:42 -06:00
|
|
|
|
chipset_device_create(struct visor_device *dev_info)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
2016-02-23 08:01:53 -07:00
|
|
|
|
int rc;
|
2015-06-04 07:22:42 -06:00
|
|
|
|
u32 bus_no = dev_info->chipset_bus_no;
|
|
|
|
|
u32 dev_no = dev_info->chipset_dev_no;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no,
|
|
|
|
|
POSTCODE_SEVERITY_INFO);
|
|
|
|
|
|
2015-06-04 07:22:42 -06:00
|
|
|
|
rc = create_visor_device(dev_info);
|
2015-06-12 14:46:08 -06:00
|
|
|
|
if (chipset_responders.device_create)
|
|
|
|
|
chipset_responders.device_create(dev_info, rc);
|
|
|
|
|
|
|
|
|
|
if (rc < 0)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
|
|
|
|
|
POSTCODE_SEVERITY_ERR);
|
2015-06-12 14:46:08 -06:00
|
|
|
|
else
|
|
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, dev_no, bus_no,
|
|
|
|
|
POSTCODE_SEVERITY_INFO);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2015-06-04 07:22:42 -06:00
|
|
|
|
chipset_device_destroy(struct visor_device *dev_info)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
2015-06-04 07:22:42 -06:00
|
|
|
|
remove_visor_device(dev_info);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
if (chipset_responders.device_destroy)
|
2015-06-04 07:22:42 -06:00
|
|
|
|
(*chipset_responders.device_destroy) (dev_info, 0);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This is the callback function specified for a function driver, to
|
|
|
|
|
* be called when a pending "pause device" operation has been
|
|
|
|
|
* completed.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
2015-06-04 07:22:42 -06:00
|
|
|
|
pause_state_change_complete(struct visor_device *dev, int status)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
|
|
|
|
if (!dev->pausing)
|
2015-07-13 12:52:26 -06:00
|
|
|
|
return;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-05-05 16:37:01 -06:00
|
|
|
|
dev->pausing = false;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
if (!chipset_responders.device_pause) /* this can never happen! */
|
2015-07-13 12:52:26 -06:00
|
|
|
|
return;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
/* Notify the chipset driver that the pause is complete, which
|
2016-02-08 08:41:49 -07:00
|
|
|
|
* will presumably want to send some sort of response to the
|
|
|
|
|
* initiator.
|
|
|
|
|
*/
|
2015-06-04 07:22:42 -06:00
|
|
|
|
(*chipset_responders.device_pause) (dev, status);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This is the callback function specified for a function driver, to
|
|
|
|
|
* be called when a pending "resume device" operation has been
|
|
|
|
|
* completed.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
2015-06-04 07:22:42 -06:00
|
|
|
|
resume_state_change_complete(struct visor_device *dev, int status)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
|
|
|
|
if (!dev->resuming)
|
2015-07-13 12:52:26 -06:00
|
|
|
|
return;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-05-05 16:37:01 -06:00
|
|
|
|
dev->resuming = false;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
if (!chipset_responders.device_resume) /* this can never happen! */
|
2015-07-13 12:52:26 -06:00
|
|
|
|
return;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
/* Notify the chipset driver that the resume is complete,
|
|
|
|
|
* which will presumably want to send some sort of response to
|
2015-11-09 21:12:00 -07:00
|
|
|
|
* the initiator.
|
|
|
|
|
*/
|
2015-06-04 07:22:42 -06:00
|
|
|
|
(*chipset_responders.device_resume) (dev, status);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
2015-06-04 07:22:42 -06:00
|
|
|
|
initiate_chipset_device_pause_resume(struct visor_device *dev, bool is_pause)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
2016-02-23 08:01:54 -07:00
|
|
|
|
int rc;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
struct visor_driver *drv = NULL;
|
2015-06-04 07:22:42 -06:00
|
|
|
|
void (*notify_func)(struct visor_device *dev, int response) = NULL;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
if (is_pause)
|
|
|
|
|
notify_func = chipset_responders.device_pause;
|
|
|
|
|
else
|
|
|
|
|
notify_func = chipset_responders.device_resume;
|
|
|
|
|
if (!notify_func)
|
2016-02-23 08:01:54 -07:00
|
|
|
|
return;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
drv = to_visor_driver(dev->device.driver);
|
2016-02-23 08:01:54 -07:00
|
|
|
|
if (!drv) {
|
|
|
|
|
(*notify_func)(dev, -ENODEV);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2016-02-23 08:01:54 -07:00
|
|
|
|
if (dev->pausing || dev->resuming) {
|
|
|
|
|
(*notify_func)(dev, -EBUSY);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
|
|
|
|
/* 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) {
|
2016-02-23 08:01:54 -07:00
|
|
|
|
if (!drv->pause) {
|
|
|
|
|
(*notify_func)(dev, -EINVAL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-05-05 16:37:01 -06:00
|
|
|
|
dev->pausing = true;
|
2016-02-23 08:01:54 -07:00
|
|
|
|
rc = drv->pause(dev, pause_state_change_complete);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
} 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
|
2015-11-09 21:12:00 -07:00
|
|
|
|
* would never even get here in that case.
|
|
|
|
|
*/
|
2015-05-05 16:36:00 -06:00
|
|
|
|
fix_vbus_dev_info(dev);
|
2016-02-23 08:01:54 -07:00
|
|
|
|
if (!drv->resume) {
|
|
|
|
|
(*notify_func)(dev, -EINVAL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2015-05-05 16:37:01 -06:00
|
|
|
|
dev->resuming = true;
|
2016-02-23 08:01:54 -07:00
|
|
|
|
rc = drv->resume(dev, resume_state_change_complete);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
2016-02-23 08:01:54 -07:00
|
|
|
|
if (rc < 0) {
|
2015-05-05 16:36:00 -06:00
|
|
|
|
if (is_pause)
|
2015-05-05 16:37:01 -06:00
|
|
|
|
dev->pausing = false;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
else
|
2015-05-05 16:37:01 -06:00
|
|
|
|
dev->resuming = false;
|
2016-02-23 08:01:54 -07:00
|
|
|
|
(*notify_func)(dev, -EINVAL);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2015-06-04 07:22:42 -06:00
|
|
|
|
chipset_device_pause(struct visor_device *dev_info)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
2015-05-13 11:22:25 -06:00
|
|
|
|
initiate_chipset_device_pause_resume(dev_info, true);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2015-06-04 07:22:42 -06:00
|
|
|
|
chipset_device_resume(struct visor_device *dev_info)
|
2015-05-05 16:36:00 -06:00
|
|
|
|
{
|
2015-05-13 11:22:25 -06:00
|
|
|
|
initiate_chipset_device_pause_resume(dev_info, false);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct channel_size_info {
|
|
|
|
|
uuid_le guid;
|
|
|
|
|
unsigned long min_size;
|
|
|
|
|
unsigned long max_size;
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-05 16:37:02 -06:00
|
|
|
|
int
|
2015-05-05 16:36:00 -06:00
|
|
|
|
visorbus_init(void)
|
|
|
|
|
{
|
2016-03-12 19:27:10 -07:00
|
|
|
|
int err;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2016-03-12 19:27:10 -07:00
|
|
|
|
POSTCODE_LINUX_3(DRIVER_ENTRY_PC, 0, POSTCODE_SEVERITY_INFO);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
bus_device_info_init(&clientbus_driverinfo,
|
2015-05-05 16:36:14 -06:00
|
|
|
|
"clientbus", "visorbus",
|
2015-05-05 16:36:00 -06:00
|
|
|
|
VERSION, NULL);
|
|
|
|
|
|
2016-03-12 19:27:10 -07:00
|
|
|
|
err = create_bus_type();
|
|
|
|
|
if (err < 0) {
|
2015-05-05 16:36:00 -06:00
|
|
|
|
POSTCODE_LINUX_2(BUS_CREATE_ENTRY_PC, DIAG_SEVERITY_ERR);
|
2016-03-12 19:27:10 -07:00
|
|
|
|
goto error;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
periodic_dev_workqueue = create_singlethread_workqueue("visorbus_dev");
|
|
|
|
|
if (!periodic_dev_workqueue) {
|
|
|
|
|
POSTCODE_LINUX_2(CREATE_WORKQUEUE_PC, DIAG_SEVERITY_ERR);
|
2016-03-12 19:27:10 -07:00
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto error;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This enables us to receive notifications when devices appear for
|
|
|
|
|
* which this service partition is to be a server for.
|
|
|
|
|
*/
|
2015-05-05 16:36:39 -06:00
|
|
|
|
visorchipset_register_busdev(&chipset_notifiers,
|
|
|
|
|
&chipset_responders,
|
|
|
|
|
&chipset_driverinfo);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2016-03-12 19:27:10 -07:00
|
|
|
|
return 0;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
|
2016-03-12 19:27:10 -07:00
|
|
|
|
error:
|
|
|
|
|
POSTCODE_LINUX_3(CHIPSET_INIT_FAILURE_PC, err, POSTCODE_SEVERITY_ERR);
|
|
|
|
|
return err;
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-05 16:36:15 -06:00
|
|
|
|
void
|
2015-05-05 16:36:00 -06:00
|
|
|
|
visorbus_exit(void)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *listentry, *listtmp;
|
|
|
|
|
|
2015-05-05 16:36:39 -06:00
|
|
|
|
visorchipset_register_busdev(NULL, NULL, NULL);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
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) {
|
2015-06-04 07:22:37 -06:00
|
|
|
|
struct visor_device *dev = list_entry(listentry,
|
2015-07-13 12:52:26 -06:00
|
|
|
|
struct visor_device,
|
|
|
|
|
list_all);
|
2015-06-04 07:22:37 -06:00
|
|
|
|
remove_bus_instance(dev);
|
2015-05-05 16:36:00 -06:00
|
|
|
|
}
|
|
|
|
|
remove_bus_type();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module_param_named(debug, visorbus_debug, int, S_IRUGO);
|
|
|
|
|
MODULE_PARM_DESC(visorbus_debug, "1 to debug");
|
|
|
|
|
|
|
|
|
|
module_param_named(forcematch, visorbus_forcematch, int, S_IRUGO);
|
|
|
|
|
MODULE_PARM_DESC(visorbus_forcematch,
|
|
|
|
|
"1 to force a successful dev <--> drv match");
|
|
|
|
|
|
|
|
|
|
module_param_named(forcenomatch, visorbus_forcenomatch, int, S_IRUGO);
|
|
|
|
|
MODULE_PARM_DESC(visorbus_forcenomatch,
|
|
|
|
|
"1 to force an UNsuccessful dev <--> drv match");
|
|
|
|
|
|
|
|
|
|
module_param_named(debugref, visorbus_debugref, int, S_IRUGO);
|
|
|
|
|
MODULE_PARM_DESC(visorbus_debugref, "1 to debug reference counting");
|