aboutsummaryrefslogtreecommitdiffstats
path: root/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_probe.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_probe.c')
-rw-r--r--linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_probe.c870
1 files changed, 870 insertions, 0 deletions
diff --git a/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_probe.c b/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_probe.c
new file mode 100644
index 0000000000..2ee59a3084
--- /dev/null
+++ b/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_probe.c
@@ -0,0 +1,870 @@
+/******************************************************************************
+ * Talks to Xen Store to figure out what devices we have.
+ * Currently experiment code, but when I grow up I'll be a bus driver!
+ *
+ * Copyright (C) 2005 Rusty Russell, IBM Corporation
+ * Copyright (C) 2005 Mike Wray, Hewlett-Packard
+ *
+ * This file may be distributed separately from the Linux kernel, or
+ * incorporated into other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include <asm-xen/hypervisor.h>
+#include <asm-xen/xenbus.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/fcntl.h>
+#include <stdarg.h>
+#include "xenbus_comms.h"
+
+/* Directory inside a domain containing devices. */
+#define XENBUS_DEVICE_DIR "device"
+
+/* Directory inside a domain containing backends. */
+#define XENBUS_BACKEND_DIR "backend"
+
+/* Name of field containing device id. */
+#define XENBUS_DEVICE_ID "id"
+
+/* Name of field containing device type. */
+#define XENBUS_DEVICE_TYPE "type"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define dprintf(_fmt, _args...) \
+printk(KERN_INFO __stringify(KBUILD_MODNAME) " [DBG] %s" _fmt, __FUNCTION__, ##_args)
+#else
+#define dprintf(_fmt, _args...) do { } while(0)
+#endif
+
+static int xs_init_done = 0;
+
+/* Return the path to dir with /name appended.
+ * If name is null or empty returns a copy of dir.
+ */
+char *xenbus_path(const char *dir, const char *name)
+{
+ char *ret;
+ if(name && strlen(name)){
+ ret = kmalloc(strlen(dir) + 1 + strlen(name) + 1, GFP_KERNEL);
+ if (!ret)
+ return NULL;
+ strcpy(ret, dir);
+ strcat(ret, "/");
+ strcat(ret, name);
+ } else {
+ ret = kmalloc(strlen(dir) + 1, GFP_KERNEL);
+ if (!ret)
+ return NULL;
+ strcpy(ret, dir);
+ }
+ return ret;
+}
+
+#define streq(a, b) (strcmp((a), (b)) == 0)
+
+char *xenbus_read(const char *dir, const char *name, unsigned int *data_n)
+{
+ int err = 0;
+ char *data = NULL;
+ char *path = xenbus_path(dir, name);
+ int n = 0;
+
+ if (!path) {
+ err = -ENOMEM;
+ goto out;
+ }
+ data = xs_read(path, &n);
+ if (IS_ERR(data)){
+ err = PTR_ERR(data);
+ if(err == -EISDIR){
+ err = -ENOENT;
+ }
+ } else if(n == 0){
+ err = -ENOENT;
+ kfree(data);
+ } else if(data[n - 1] != '\0') {
+ /* This shouldn't happen: everything is supposed to be a string. */
+ printk("XENBUS: Reading path %s: missing null terminator len=%i\n", path, n);
+ err = -EINVAL;
+ kfree(data);
+ n = 0;
+ }
+ kfree(path);
+ out:
+ if(data_n)
+ *data_n = n;
+ return (err ? ERR_PTR(err) : data);
+}
+
+int xenbus_write(const char *dir, const char *name, const char *data, int data_n)
+{
+ int err = 0;
+ char *path = xenbus_path(dir, name);
+
+ if (!path)
+ return -ENOMEM;
+ err = xs_write(path, data, data_n, O_CREAT);
+ kfree(path);
+ return err;
+}
+
+int xenbus_read_string(const char *dir, const char *name, char **val)
+{
+ int err = 0;
+
+ *val = xenbus_read(dir, name, NULL);
+ if (IS_ERR(*val)) {
+ err = PTR_ERR(*val);
+ *val = NULL;
+ }
+ return err;
+}
+
+int xenbus_write_string(const char *dir, const char *name, const char *val)
+{
+ return xenbus_write(dir, name, val, strlen(val) + 1);
+}
+
+int xenbus_read_ulong(const char *dir, const char *name, unsigned long *val)
+{
+ int err = 0;
+ char *data = NULL, *end = NULL;
+ unsigned int data_n = 0;
+
+ data = xenbus_read(dir, name, &data_n);
+ if (IS_ERR(data)) {
+ err = PTR_ERR(data);
+ goto out;
+ }
+ if (data_n <= 1) {
+ err = -ENOENT;
+ goto free_data;
+ }
+ *val = simple_strtoul(data, &end, 10);
+ if (end != data + data_n - 1) {
+ printk("XENBUS: Path %s/%s, bad parse of '%s' as ulong\n",
+ dir, name, data);
+ err = -EINVAL;
+ }
+ free_data:
+ kfree(data);
+ out:
+ if (err)
+ *val = 0;
+ return err;
+}
+
+int xenbus_write_ulong(const char *dir, const char *name, unsigned long val)
+{
+ char data[32] = {};
+
+ snprintf(data, sizeof(data), "%lu", val);
+ return xenbus_write(dir, name, data, strlen(data) + 1);
+}
+
+int xenbus_read_long(const char *dir, const char *name, long *val)
+{
+ int err = 0;
+ char *data = NULL, *end = NULL;
+ unsigned int data_n = 0;
+
+ data = xenbus_read(dir, name, &data_n);
+ if (IS_ERR(data)) {
+ err = PTR_ERR(data);
+ goto out;
+ }
+ if (data_n <= 1) {
+ err = -ENOENT;
+ goto free_data;
+ }
+ *val = simple_strtol(data, &end, 10);
+ if (end != data + data_n - 1) {
+ printk("XENBUS: Path %s/%s, bad parse of '%s' as long\n",
+ dir, name, data);
+ err = -EINVAL;
+ }
+ free_data:
+ kfree(data);
+ out:
+ if (err)
+ *val = 0;
+ return err;
+}
+
+int xenbus_write_long(const char *dir, const char *name, long val)
+{
+ char data[32] = {};
+
+ snprintf(data, sizeof(data), "%li", val);
+ return xenbus_write(dir, name, data, strlen(data) + 1);
+}
+
+/* Number of characters in string form of a MAC address. */
+#define MAC_LENGTH 17
+
+/** Convert a mac address from a string of the form
+ * XX:XX:XX:XX:XX:XX to numerical form (an array of 6 unsigned chars).
+ * Each X denotes a hex digit: 0..9, a..f, A..F.
+ * Also supports using '-' as the separator instead of ':'.
+ */
+static int mac_aton(const char *macstr, unsigned int n, unsigned char mac[6]){
+ int err = -EINVAL;
+ int i, j;
+ const char *p;
+ char sep = 0;
+
+ if(!macstr || n != MAC_LENGTH){
+ goto exit;
+ }
+ for(i = 0, p = macstr; i < 6; i++){
+ unsigned char d = 0;
+ if(i){
+ if(!sep){
+ if(*p == ':' || *p == '-') sep = *p;
+ }
+ if(sep && *p == sep){
+ p++;
+ } else {
+ goto exit;
+ }
+ }
+ for(j = 0; j < 2; j++, p++){
+ if(j) d <<= 4;
+ if(isdigit(*p)){
+ d += *p - '0';
+ } else if(isxdigit(*p)){
+ d += toupper(*p) - 'A' + 10;
+ } else {
+ goto exit;
+ }
+ }
+ mac[i] = d;
+ }
+ err = 0;
+ exit:
+ return err;
+}
+
+int xenbus_read_mac(const char *dir, const char *name, unsigned char mac[6])
+{
+ int err = 0;
+ char *data = 0;
+ unsigned int data_n = 0;
+
+ data = xenbus_read(dir, name, &data_n);
+ if (IS_ERR(data)) {
+ err = PTR_ERR(data);
+ goto out;
+ }
+ if (data_n <= 1) {
+ err = -ENOENT;
+ goto free_data;
+ }
+ err = mac_aton(data, data_n - 1, mac);
+ if (err) {
+ printk("XENBUS: Path %s/%s, bad parse of '%s' as mac\n",
+ dir, name, data);
+ err = -EINVAL;
+ }
+ free_data:
+ kfree(data);
+ out:
+ if(err)
+ memset(mac, 0, sizeof(mac));
+ return err;
+}
+
+int xenbus_write_mac(const char *dir, const char *name, const unsigned char mac[6])
+{
+ char buf[MAC_LENGTH + 1] = {};
+ int buf_n = sizeof(buf);
+
+ snprintf(buf, buf_n, "%02x:%02x:%02x:%02x:%02x:%02x",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ buf[buf_n - 1] = '\0';
+ return xenbus_write(dir, name, buf, buf_n);
+}
+
+/* Read event channel information from xenstore.
+ *
+ * Event channel xenstore fields:
+ * dom1 - backend domain id (int)
+ * port1 - backend port (int)
+ * dom2 - frontend domain id (int)
+ * port2 - frontend port (int)
+ */
+int xenbus_read_evtchn(const char *dir, const char *name, struct xenbus_evtchn *evtchn)
+{
+ int err = 0;
+ char *evtchn_path = xenbus_path(dir, name);
+
+ if (!evtchn_path) {
+ err = -ENOMEM;
+ goto out;
+ }
+ err = xenbus_read_ulong(evtchn_path, "dom1", &evtchn->dom1);
+ if(err)
+ goto free_evtchn_path;
+ err = xenbus_read_ulong(evtchn_path, "port1", &evtchn->port1);
+ if(err)
+ goto free_evtchn_path;
+ err = xenbus_read_ulong(evtchn_path, "dom2", &evtchn->dom2);
+ if(err)
+ goto free_evtchn_path;
+ err = xenbus_read_ulong(evtchn_path, "port2", &evtchn->port2);
+
+ free_evtchn_path:
+ kfree(evtchn_path);
+ out:
+ if (err)
+ *evtchn = (struct xenbus_evtchn){};
+ return err;
+}
+
+/* Write a message to 'dir'.
+ * The data is 'val' followed by parameter names and values,
+ * terminated by NULL.
+ */
+int xenbus_message(const char *dir, const char *val, ...)
+{
+ static const char *mid_name = "@mid";
+ va_list args;
+ int err = 0;
+ char *mid_path = NULL;
+ char *msg_path = NULL;
+ char mid_str[32] = {};
+ long mid = 0;
+ int i;
+
+ va_start(args, val);
+ mid_path = xenbus_path(dir, mid_name);
+ if (!mid_path) {
+ err = -ENOMEM;
+ goto out;
+ }
+ err = xenbus_read_long(dir, mid_name, &mid);
+ if(err != -ENOENT)
+ goto out;
+ mid++;
+ err = xenbus_write_long(dir, mid_name, mid);
+ if(err)
+ goto out;
+ sprintf(mid_str, "%li", mid);
+ msg_path = xenbus_path(dir, mid_str);
+ if (!mid_path) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for(i = 0; i < 16; i++){
+ char *k, *v;
+ k = va_arg(args, char *);
+ if(!k)
+ break;
+ v = va_arg(args, char *);
+ if(!v)
+ break;
+ err = xenbus_write_string(msg_path, k, v);
+ if(err)
+ goto out;
+ }
+ err = xenbus_write_string(msg_path, NULL, val);
+
+ out:
+ kfree(msg_path);
+ kfree(mid_path);
+ va_end(args);
+ return err;
+}
+
+/* If something in array of ids matches this device, return it. */
+static const struct xenbus_device_id *
+match_device(const struct xenbus_device_id *arr, struct xenbus_device *dev)
+{
+ for( ; !streq(arr->devicetype, ""); arr++) {
+ if (!streq(arr->devicetype, dev->devicetype))
+ continue;
+
+ if (streq(arr->subtype, "")
+ || streq(arr->subtype, dev->subtype)) {
+ return arr;
+ }
+ }
+ return NULL;
+}
+
+static int xenbus_match(struct device *_dev, struct device_driver *_drv)
+{
+ struct xenbus_driver *drv = to_xenbus_driver(_drv);
+
+ if (!drv->ids)
+ return 0;
+
+ return match_device(drv->ids, to_xenbus_device(_dev)) != NULL;
+}
+
+/* Bus type for frontend drivers. */
+static struct bus_type xenbus_type = {
+ .name = "xenbus",
+ .match = xenbus_match,
+};
+
+
+/* Bus type for backend drivers. */
+static struct bus_type xenback_type = {
+ .name = "xenback",
+ .match = xenbus_match,
+};
+
+struct xenbus_for_dev {
+ int (*fn)(struct xenbus_device *, void *);
+ void *data;
+};
+
+static int for_dev(struct device *_dev, void *_data)
+{
+ struct xenbus_device *dev = to_xenbus_device(_dev);
+ struct xenbus_for_dev *data = _data;
+ dev = to_xenbus_device(_dev);
+ return data->fn(dev, data->data);
+}
+
+int xenbus_for_each_dev(struct xenbus_device * start, void * data,
+ int (*fn)(struct xenbus_device *, void *))
+{
+ struct xenbus_for_dev for_data = {
+ .fn = fn,
+ .data = data,
+ };
+ if(!fn)
+ return -EINVAL;
+ printk("%s> data=%p fn=%p for_data=%p\n", __FUNCTION__,
+ data, fn, &for_data);
+ return bus_for_each_dev(&xenbus_type,
+ (start ? &start->dev : NULL),
+ &for_data, for_dev);
+}
+
+struct xenbus_for_drv {
+ int (*fn)(struct xenbus_driver *, void *);
+ void *data;
+};
+
+static int for_drv(struct device_driver *_drv, void *_data)
+{
+ struct xenbus_driver *drv = to_xenbus_driver(_drv);
+ struct xenbus_for_drv *data = _data;
+ return data->fn(drv, data->data);
+}
+
+int xenbus_for_each_drv(struct xenbus_driver * start, void * data,
+ int (*fn)(struct xenbus_driver *, void *))
+{
+ struct xenbus_for_drv for_data = {
+ .fn = fn,
+ .data = data,
+ };
+ if(!fn)
+ return -EINVAL;
+ return bus_for_each_drv(&xenbus_type,
+ (start ? &start->driver: NULL),
+ &for_data, for_drv);
+}
+
+static int xenbus_dev_probe(struct device *_dev)
+{
+ struct xenbus_device *dev = to_xenbus_device(_dev);
+ struct xenbus_driver *drv = to_xenbus_driver(_dev->driver);
+ const struct xenbus_device_id *id;
+
+ if (!drv->probe)
+ return -ENODEV;
+
+ id = match_device(drv->ids, dev);
+ if (!id)
+ return -ENODEV;
+ return drv->probe(dev, id);
+}
+
+static int xenbus_dev_remove(struct device *_dev)
+{
+ struct xenbus_device *dev = to_xenbus_device(_dev);
+ struct xenbus_driver *drv = to_xenbus_driver(_dev->driver);
+
+ if(!drv->remove)
+ return 0;
+ return drv->remove(dev);
+}
+
+int xenbus_register_driver(struct xenbus_driver *drv)
+{
+ int err = 0;
+
+ printk("%s> frontend driver %p %s\n", __FUNCTION__,
+ drv, drv->name);
+ drv->driver.name = drv->name;
+ drv->driver.bus = &xenbus_type;
+ drv->driver.owner = drv->owner;
+ drv->driver.probe = xenbus_dev_probe;
+ drv->driver.remove = xenbus_dev_remove;
+
+ err = driver_register(&drv->driver);
+ if(err == 0 && xs_init_done && drv->connect){
+ printk("%s> connecting driver %p %s\n", __FUNCTION__,
+ drv, drv->name);
+ drv->connect(drv);
+ }
+ return err;
+}
+
+void xenbus_unregister_driver(struct xenbus_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+
+static int xenbus_probe_device(const char *dir, const char *name)
+{
+ int err;
+ struct xenbus_device *xendev;
+ unsigned int xendev_n;
+ long id;
+ char *nodename, *devicetype;
+ unsigned int devicetype_n;
+
+ dprintf("> dir=%s name=%s\n", dir, name);
+ nodename = xenbus_path(dir, name);
+ if (!nodename)
+ return -ENOMEM;
+
+ devicetype = xenbus_read(nodename, XENBUS_DEVICE_TYPE, &devicetype_n);
+ if (IS_ERR(devicetype)) {
+ err = PTR_ERR(devicetype);
+ goto free_nodename;
+ }
+
+ err = xenbus_read_long(nodename, XENBUS_DEVICE_ID, &id);
+ if (err == -ENOENT) {
+ id = 0;
+ } else if (err != 0) {
+ goto free_devicetype;
+ }
+
+ dprintf("> devicetype='%s' name='%s' id=%ld\n", devicetype, name, id);
+ /* FIXME: This could be a rescan. Don't re-register existing devices. */
+
+ /* Add space for the strings. */
+ xendev_n = sizeof(*xendev) + strlen(nodename) + strlen(devicetype) + 2;
+ xendev = kmalloc(xendev_n, GFP_KERNEL);
+ if (!xendev) {
+ err = -ENOMEM;
+ goto free_devicetype;
+ }
+ memset(xendev, 0, xendev_n);
+
+ snprintf(xendev->dev.bus_id, BUS_ID_SIZE, "%s-%s", devicetype, name);
+ xendev->dev.bus = &xenbus_type;
+
+ xendev->id = id;
+
+ /* Copy the strings into the extra space. */
+ xendev->nodename = (char *)(xendev + 1);
+ strcpy(xendev->nodename, nodename);
+ xendev->devicetype = xendev->nodename + strlen(xendev->nodename) + 1;
+ strcpy(xendev->devicetype, devicetype);
+
+ /* Register with generic device framework. */
+ printk("XENBUS: Registering device %s\n", xendev->dev.bus_id);
+ err = device_register(&xendev->dev);
+ if (err) {
+ printk("XENBUS: Registering device %s: error %i\n",
+ xendev->dev.bus_id, err);
+ }
+ if (err)
+ kfree(xendev);
+
+free_devicetype:
+ kfree(devicetype);
+free_nodename:
+ kfree(nodename);
+ dprintf("< err=%i\n", err);
+ return err;
+}
+
+static int xenbus_probe_device_type(const char *dirpath, const char *typename)
+{
+ int err = 0;
+ char **dir;
+ char *path;
+ unsigned int dir_n = 0;
+ int i;
+
+ dprintf("> dirpath=%s typename=%s\n", dirpath, typename);
+ path = xenbus_path(dirpath, typename);
+ if (!path)
+ return -ENOMEM;
+
+ dir = xs_directory(path, &dir_n);
+ if (IS_ERR(dir)) {
+ err = PTR_ERR(dir);
+ goto out;
+ }
+
+ for (i = 0; i < dir_n; i++) {
+ err = xenbus_probe_device(path, dir[i]);
+ if (err)
+ break;
+ }
+ kfree(dir);
+out:
+ kfree(path);
+ dprintf("< err=%i\n", err);
+ return err;
+}
+
+static int xenbus_probe_devices(const char *path)
+{
+ int err = 0;
+ char **dir;
+ unsigned int i, dir_n;
+
+ dprintf("> path=%s\n", path);
+ down(&xs_lock);
+ dir = xs_directory(path, &dir_n);
+ if (IS_ERR(dir)) {
+ err = PTR_ERR(dir);
+ goto unlock;
+ }
+ for (i = 0; i < dir_n; i++) {
+ err = xenbus_probe_device_type(path, dir[i]);
+ if (err)
+ break;
+ }
+ kfree(dir);
+unlock:
+ up(&xs_lock);
+ dprintf("< err=%i\n", err);
+ return err;
+}
+
+
+static int xenbus_probe_backend(const char *dir, const char *name)
+{
+ int err = 0;
+ struct xenbus_device *xendev = NULL;
+ unsigned int xendev_n = 0;
+ char *nodename = NULL, *devicetype = NULL;
+ unsigned int devicetype_n = 0;
+
+ dprintf("> dir=%s name=%s\n", dir, name);
+ nodename = xenbus_path(dir, name);
+ if (!nodename)
+ return -ENOMEM;
+
+ devicetype = xenbus_read(nodename, XENBUS_DEVICE_TYPE, &devicetype_n);
+ if (IS_ERR(devicetype)) {
+ err = PTR_ERR(devicetype);
+ goto free_nodename;
+ }
+
+ dprintf("> devicetype='%s'\n", devicetype);
+ /* FIXME: This could be a rescan. Don't re-register existing devices. */
+
+ /* Add space for the strings. */
+ xendev_n = sizeof(*xendev) + strlen(nodename) + strlen(devicetype) + 2;
+ xendev = kmalloc(xendev_n, GFP_KERNEL);
+ if (!xendev) {
+ err = -ENOMEM;
+ goto free_devicetype;
+ }
+ memset(xendev, 0, xendev_n);
+
+ snprintf(xendev->dev.bus_id, BUS_ID_SIZE, "%s", devicetype);
+ xendev->dev.bus = &xenback_type;
+
+ /* Copy the strings into the extra space. */
+ xendev->nodename = (char *)(xendev + 1);
+ strcpy(xendev->nodename, nodename);
+ xendev->devicetype = xendev->nodename + strlen(xendev->nodename) + 1;
+ strcpy(xendev->devicetype, devicetype);
+
+ /* Register with generic device framework. */
+ printk("XENBUS: Registering backend %s\n", xendev->dev.bus_id);
+ err = device_register(&xendev->dev);
+ if (err) {
+ printk("XENBUS: Registering device %s: error %i\n",
+ xendev->dev.bus_id, err);
+ }
+ if (err)
+ kfree(xendev);
+
+free_devicetype:
+ kfree(devicetype);
+free_nodename:
+ kfree(nodename);
+ dprintf("< err=%i\n", err);
+ return err;
+}
+
+static int xenbus_probe_backends(const char *path)
+{
+ int err = 0;
+ char **dir;
+ unsigned int i, dir_n;
+
+ dprintf("> path=%s\n", path);
+ down(&xs_lock);
+ dir = xs_directory(path, &dir_n);
+ if (IS_ERR(dir)) {
+ err = PTR_ERR(dir);
+ goto unlock;
+ }
+ for (i = 0; i < dir_n; i++) {
+ err = xenbus_probe_backend(path, dir[i]);
+ if (err)
+ break;
+ }
+ kfree(dir);
+unlock:
+ up(&xs_lock);
+ dprintf("< err=%i\n", err);
+ return err;
+}
+
+int xenbus_register_backend(struct xenbus_driver *drv)
+{
+ int err = 0;
+
+ printk("%s> backend driver %p %s\n", __FUNCTION__,
+ drv, drv->name);
+ drv->driver.name = drv->name;
+ drv->driver.bus = &xenback_type;
+ drv->driver.owner = drv->owner;
+ drv->driver.probe = xenbus_dev_probe;
+ drv->driver.remove = xenbus_dev_remove;
+
+ err = driver_register(&drv->driver);
+ if(err == 0 && xs_init_done && drv->connect){
+ printk("%s> connecting driver %p %s\n", __FUNCTION__,
+ drv, drv->name);
+ drv->connect(drv);
+ }
+ return err;
+}
+
+void xenbus_unregister_backend(struct xenbus_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+
+int xenbus_for_each_backend(struct xenbus_driver * start, void * data,
+ int (*fn)(struct xenbus_driver *, void *))
+{
+ struct xenbus_for_drv for_data = {
+ .fn = fn,
+ .data = data,
+ };
+ if(!fn)
+ return -EINVAL;
+ return bus_for_each_drv(&xenback_type,
+ (start ? &start->driver: NULL),
+ &for_data, for_drv);
+}
+
+static void test_callback(struct xenbus_watch *w, const char *node)
+{
+ printk("test_callback: got watch hit for %s\n", node);
+}
+
+static void test_watch(void)
+{
+ static int init_done = 0;
+ static struct xenbus_watch watch = { .node = "/",
+ .priority = 0,
+ .callback = test_callback };
+
+ if(init_done) return;
+ printk("registering watch %lX = %i\n",
+ (long)&watch,
+ register_xenbus_watch(&watch));
+ init_done = 1;
+}
+
+static int xenbus_driver_connect(struct xenbus_driver *drv, void *data)
+{
+ printk("%s> driver %p %s\n", __FUNCTION__, drv, drv->name);
+ if (drv->connect) {
+ printk("%s> connecting driver %p %s\n", __FUNCTION__,
+ drv, drv->name);
+ drv->connect(drv);
+ }
+ printk("%s< driver %p %s\n", __FUNCTION__, drv, drv->name);
+ return 0;
+}
+
+int do_xenbus_connect(void *unused)
+{
+ int err = 0;
+
+ printk("%s> xs_init_done=%d\n", __FUNCTION__, xs_init_done);
+ if (xs_init_done)
+ goto exit;
+ /* Initialize xenstore comms unless already done. */
+ printk("store_evtchn = %i\n", xen_start_info.store_evtchn);
+ err = xs_init();
+ if (err) {
+ printk("XENBUS: Error initializing xenstore comms:"
+ " %i\n", err);
+ goto exit;
+ }
+ xs_init_done = 1;
+
+ /* Notify drivers that xenstore has connected. */
+ test_watch();
+ printk("%s> connect drivers...\n", __FUNCTION__);
+ xenbus_for_each_drv(NULL, NULL, xenbus_driver_connect);
+ printk("%s> connect backends...\n", __FUNCTION__);
+ xenbus_for_each_backend(NULL, NULL, xenbus_driver_connect);
+
+ /* Enumerate devices and backends in xenstore. */
+ xenbus_probe_devices(XENBUS_DEVICE_DIR);
+ xenbus_probe_backends(XENBUS_BACKEND_DIR);
+
+exit:
+ printk("%s< err=%d\n", __FUNCTION__, err);
+ return err;
+}
+
+static int __init xenbus_probe_init(void)
+{
+ bus_register(&xenbus_type);
+ bus_register(&xenback_type);
+
+ if (!xen_start_info.store_evtchn)
+ return 0;
+
+ do_xenbus_connect(NULL);
+ return 0;
+}
+
+postcore_initcall(xenbus_probe_init);