[PATCH][2.6] set up vio_dev's driver field

Hollis Blanchard hollisb at us.ibm.com
Wed Jan 7 10:26:09 EST 2004


On Jan 6, 2004, at 4:44 PM, Linda Xie wrote:
> diff -Nru a/arch/ppc64/kernel/vio.c b/arch/ppc64/kernel/vio.c
> --- a/arch/ppc64/kernel/vio.c	Tue Jan  6 16:29:17 2004
> +++ b/arch/ppc64/kernel/vio.c	Tue Jan  6 16:29:17 2004
> @@ -189,7 +189,7 @@
>  		const struct vio_device_id* id;
>
>  		id = vio_match_device(driver->id_table, dev);
> -		if (id && (0 < driver->probe(dev, id))) {
> +		if (id && (0 == driver->probe(dev, id))) {
>  			printk(KERN_DEBUG "%s: driver %s/%s took device %p\n",
>  				__FUNCTION__, id->type, id->compat, dev);
>  			dev->driver = driver;

You're right that the drivers return 0 on success, but all this code is
about to be replaced with 2.6 driver model code anyways. The driver
model gives us basic sysfs presense and list locking for free.

Could you test this patch instead? It should require no driver changes.
(I don't think the patch will be whitespace-wrapped but let me know.)

Comments from Greg KH also welcome, though Linda's mail prompted me to
send this out before I've double-checked everything. :) In particular I
had to create a static struct device to act as the VIO bus device,
since the virtual bus doesn't have an actual root struct device (unlike
PCI and USB)...

--
Hollis Blanchard
IBM Linux Technology Center
-------------- next part --------------
===== arch/ppc64/kernel/vio.c 1.4 vs edited =====
--- 1.4/arch/ppc64/kernel/vio.c	Fri Dec  5 18:09:27 2003
+++ edited/arch/ppc64/kernel/vio.c	Tue Jan  6 17:19:12 2004
@@ -4,6 +4,7 @@
   *    Copyright (c) 2003 IBM Corp.
   *     Dave Engebretsen engebret at us.ibm.com
   *     Santiago Leon santil at us.ibm.com
+ *     Hollis Blanchard <hollisb at us.ibm.com>
   *
   *      This program is free software; you can redistribute it and/or
   *      modify it under the terms of the GNU General Public License
@@ -16,6 +17,7 @@
  #include <linux/pci.h>
  #include <linux/version.h>
  #include <linux/module.h>
+#include <linux/kobject.h>
  #include <asm/rtas.h>
  #include <asm/pci_dma.h>
  #include <asm/dma.h>
@@ -26,6 +28,8 @@
  #include <linux/mm.h>
  #endif

+#define DBGENTER() pr_debug("%s entered\n", __FUNCTION__)
+
  extern struct TceTable *build_tce_table(struct TceTable *tbl);

  extern dma_addr_t get_tces(struct TceTable *, unsigned order,
@@ -33,83 +37,75 @@
  extern void tce_free(struct TceTable *tbl, dma_addr_t dma_addr,
  		     unsigned order, unsigned num_pages);

+static struct device vio_bus_device; /* for viodev->dev.parent */
+static int vio_num_address_cells;

-static struct vio_bus vio_bus;
-static LIST_HEAD(registered_vio_drivers);
-int vio_num_address_cells;
-EXPORT_SYMBOL(vio_num_address_cells);
-
-/* TODO:
- *   really fit into driver model (see include/linux/device.h)
- *   locking around list accesses
- */
+/* convert from struct device to struct vio_dev and pass to driver.
+ * dev->driver has already been set by generic code because
vio_bus_match
+ * succeeded. */
+static int vio_bus_probe(struct device *dev)
+{
+	struct vio_dev *viodev = to_vio_dev(dev);
+	struct vio_driver *viodrv = to_vio_driver(dev->driver);
+	const struct vio_device_id *id;
+	int error = -ENODEV;
+
+	DBGENTER();
+
+	if (!viodrv->probe)
+		return error;
+
+	id = vio_match_device(viodrv->id_table, viodev);
+	if (id) {
+		error = viodrv->probe(viodev, id);
+	}
+
+	return error;
+}
+
+/* convert from struct device to struct vio_dev and pass to driver. */
+static int vio_bus_remove(struct device *dev)
+{
+	struct vio_dev *viodev = to_vio_dev(dev);
+	struct vio_driver *viodrv = to_vio_driver(dev->driver);
+
+	DBGENTER();
+
+	if (viodrv->remove) {
+		return viodrv->remove(viodev);
+	}
+
+	/* driver can't remove */
+	return 1;
+}

  /**
   * vio_register_driver: - Register a new vio driver
   * @drv:	The vio_driver structure to be registered.
- *
- * Adds the driver structure to the list of registered drivers
- * Returns the number of vio devices which were claimed by the driver
- * during registration.  The driver remains registered even if the
- * return value is zero.
   */
-int vio_register_driver(struct vio_driver *drv)
+int vio_register_driver(struct vio_driver *viodrv)
  {
-	int count = 0;
-	struct vio_dev *dev;
-
-	printk(KERN_DEBUG "%s: driver %s/%s registering\n", __FUNCTION__,
-		drv->id_table[0].type, drv->id_table[0].type);
+	printk(KERN_DEBUG "%s: driver %s registering\n", __FUNCTION__,
+		viodrv->name);

-	/* find matching devices not already claimed by other drivers and pass
-	 * them to probe() */
-	list_for_each_entry(dev, &vio_bus.devices, devices_list) {
-		const struct vio_device_id* id;
-
-		if (dev->driver)
-			continue; /* this device is already owned */
-
-		id = vio_match_device(drv->id_table, dev);
-		if (drv && id) {
-			if (0 == drv->probe(dev, id)) {
-				printk(KERN_DEBUG "  took device %p\n", dev);
-				dev->driver = drv;
-				count++;
-			}
-		}
-	}
+	/* fill in 'struct device' fields */
+	viodrv->driver.name = viodrv->name;
+	viodrv->driver.bus = &vio_bus_type;
+	viodrv->driver.probe = vio_bus_probe;
+	viodrv->driver.remove = vio_bus_remove;

-	list_add_tail(&drv->node, &registered_vio_drivers);
-
-	return count;
+	return driver_register(&viodrv->driver);
  }
  EXPORT_SYMBOL(vio_register_driver);

  /**
   * vio_unregister_driver - Remove registration of vio driver.
   * @driver:	The vio_driver struct to be removed form registration
- *
- * Searches for devices that are assigned to the driver and calls
- * driver->remove() for each one.  Removes the driver from the list
- * of registered drivers.  Returns the number of devices that were
- * assigned to that driver.
   */
-int vio_unregister_driver(struct vio_driver *driver)
+int vio_unregister_driver(struct vio_driver *viodrv)
  {
-	struct vio_dev *dev;
-	int devices_found = 0;
-
-	list_for_each_entry(dev, &vio_bus.devices, devices_list) {
-		if (dev->driver == driver) {
-			driver->remove(dev);
-			dev->driver = NULL;
-			devices_found++;
-		}
-	}
-
-	list_del(&driver->node);
-
-	return devices_found;
+	driver_unregister(&viodrv->driver);
+	return 0;
  }
  EXPORT_SYMBOL(vio_unregister_driver);

@@ -125,6 +121,8 @@
  const struct vio_device_id *
  vio_match_device(const struct vio_device_id *ids, const struct vio_dev
*dev)
  {
+	DBGENTER();
+
  	while (ids->type) {
  		if ((strncmp(dev->archdata->type, ids->type, strlen(ids->type)) ==
0) &&
  			device_is_compatible((struct device_node*)dev->archdata,
ids->compat))
@@ -137,12 +135,20 @@
  /**
   * vio_bus_init: - Initialize the virtual IO bus
   */
-int __init
+static int __init
  vio_bus_init(void)
  {
  	struct device_node *node_vroot, *node_vdev;
+	int err;

-	INIT_LIST_HEAD(&vio_bus.devices);
+	err = bus_register(&vio_bus_type);
+	if (err)
+		return err;
+
+	/* the parent of all vio devices */
+	memset(&vio_bus_device, 0, sizeof(struct device));
+	strcpy(vio_bus_device.bus_id, "vio");
+	device_register(&vio_bus_device);

  	/*
  	 * Create device node entries for each virtual device
@@ -171,39 +177,21 @@
  __initcall(vio_bus_init);


-/**
- * vio_probe_device - attach dev to appropriate driver
- * @dev:	device to find a driver for
- *
- * Walks the list of registered VIO drivers looking for one to take
this
- * device.
- *
- * Returns a pointer to the matched driver or NULL if driver is not
- * found.
- */
-struct vio_driver * __devinit vio_probe_device(struct vio_dev* dev)
+/* vio_dev refcount hit 0 */
+static void __devinit vio_dev_release(struct device *dev)
  {
-	struct vio_driver *driver;
+	struct vio_dev *viodev = to_vio_dev(dev);

-	list_for_each_entry(driver, &registered_vio_drivers, node) {
-		const struct vio_device_id* id;
+	DBGENTER();

-		id = vio_match_device(driver->id_table, dev);
-		if (id && (0 < driver->probe(dev, id))) {
-			printk(KERN_DEBUG "%s: driver %s/%s took device %p\n",
-				__FUNCTION__, id->type, id->compat, dev);
-			dev->driver = driver;
-			return driver;
-		}
-	}
-
-	printk(KERN_DEBUG "%s: device %p found no driver\n", __FUNCTION__,
dev);
-	return NULL;
+	/* XXX free TCE table */
+	of_node_put(viodev->archdata);
+	kfree(viodev);
  }

  /**
   * vio_register_device: - Register a new vio device.
- * @archdata:	The OF node for this device.
+ * @node_vdev:	The OF node for this device.
   *
   * Creates and initializes a vio_dev structure from the data in
   * node_vdev (archdata) and adds it to the list of virtual devices.
@@ -212,11 +200,13 @@
   */
  struct vio_dev * __devinit vio_register_device(struct device_node
*node_vdev)
  {
-	struct vio_dev *dev;
+	struct vio_dev *viodev;
  	unsigned int *unit_address;
  	unsigned int *irq_p;

-	/* guarantee all vio_devs have 'device_type' field*/
+	DBGENTER();
+
+	/* we need the 'device_type' property, in order to match with drivers
*/
  	if ((NULL == node_vdev->type)) {
  		printk(KERN_WARNING
  			"%s: node %s missing 'device_type'\n", __FUNCTION__,
@@ -232,37 +222,46 @@
  	}

  	/* allocate a vio_dev for this node */
-	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
-	if (!dev)
+	viodev = kmalloc(sizeof(struct vio_dev), GFP_KERNEL);
+	if (!viodev) {
  		return NULL;
-	memset(dev, 0, sizeof(*dev));
+	}
+	memset(viodev, 0, sizeof(struct vio_dev));

-	dev->archdata = (void*)of_node_get(node_vdev);
-	dev->bus = &vio_bus;
-	dev->unit_address = *unit_address;
-	dev->tce_table = vio_build_tce_table(dev);
-
-	irq_p = (unsigned int *) get_property(node_vdev, "interrupts", 0);
-	if(irq_p) {
-		dev->irq = irq_offset_up(*irq_p);
-	} else {
-		dev->irq = (unsigned int) -1;
+	viodev->archdata = (void *)of_node_get(node_vdev);
+	viodev->unit_address = *unit_address;
+	viodev->tce_table = vio_build_tce_table(viodev);
+
+	viodev->irq = (unsigned int) -1;
+	irq_p = (unsigned int *)get_property(node_vdev, "interrupts", 0);
+	if (irq_p) {
+		viodev->irq = irq_offset_up(*irq_p);
  	}

-	list_add_tail(&dev->devices_list, &vio_bus.devices);
+	/* init generic 'struct device' fields: */
+	viodev->device.parent = &vio_bus_device;
+	viodev->device.bus = &vio_bus_type;
+	snprintf(viodev->device.bus_id, BUS_ID_SIZE, "%s@%lx",
+		node_vdev->name, viodev->unit_address);
+	viodev->device.release = vio_dev_release;

-	vio_probe_device(dev); /* finally, assign it to a driver */
+	/* register with generic device framework */
+	if (device_register(&viodev->device)) {
+		printk(KERN_ERR "%s: failed to register device %s\n", __FUNCTION__,
+			viodev->device.bus_id);
+	}

-	return dev;
+	return viodev;
  }
+EXPORT_SYMBOL(vio_register_device);

-int __devinit vio_unregister_device(struct vio_dev *dev)
+int __devinit vio_unregister_device(struct vio_dev *viodev)
  {
-	list_del(&dev->devices_list);
-	of_node_put(dev->archdata);
-
+	DBGENTER();
+	device_unregister(&viodev->device);
  	return 0;
  }
+EXPORT_SYMBOL(vio_unregister_device);

  /**
   * vio_get_attribute: - get attribute for virtual device
@@ -529,6 +528,30 @@
  	}
  }
  EXPORT_SYMBOL(vio_free_consistent);
+
+static int vio_bus_match(struct device *dev, struct device_driver *drv)
+{
+	const struct vio_dev *vio_dev = to_vio_dev(dev);
+	struct vio_driver *vio_drv = to_vio_driver(drv);
+	const struct vio_device_id *ids = vio_drv->id_table;
+	const struct vio_device_id *found_id;
+
+	DBGENTER();
+
+	if (!ids)
+		return 0;
+
+	found_id = vio_match_device(ids, vio_dev);
+	if (found_id)
+		return 1;
+
+	return 0;
+}
+
+struct bus_type vio_bus_type = {
+	.name = "vio",
+	.match = vio_bus_match,
+};

  EXPORT_SYMBOL(plpar_hcall_norets);
  EXPORT_SYMBOL(plpar_hcall_8arg_2ret);
===== include/asm-ppc64/vio.h 1.3 vs edited =====
--- 1.3/include/asm-ppc64/vio.h	Tue Dec 16 15:22:18 2003
+++ edited/include/asm-ppc64/vio.h	Fri Dec 19 13:57:42 2003
@@ -16,6 +16,7 @@

  #include <linux/init.h>
  #include <linux/errno.h>
+#include <linux/device.h>
  #include <asm/hvcall.h>
  #include <asm/prom.h>
  #include <asm/scatterlist.h>
@@ -64,11 +65,11 @@
  void vio_free_consistent(struct vio_dev *dev, size_t size, void *vaddr,
  			 dma_addr_t dma_handle);

+extern struct bus_type vio_bus_type;
+
  struct vio_device_id {
  	char *type;
  	char *compat;
-/* I don't think we need this
-	unsigned long driver_data;	*/ /* Data private to the driver */
  };

  struct vio_driver {
@@ -76,55 +77,60 @@
  	char *name;
  	const struct vio_device_id *id_table;	/* NULL if wants all devices */
  	int  (*probe)  (struct vio_dev *dev, const struct vio_device_id
*id);	/* New device inserted */
-	void (*remove) (struct vio_dev *dev);	/* Device removed (NULL if not
a hot-plug capable driver) */
+	int (*remove) (struct vio_dev *dev);	/* Device removed (NULL if not a
hot-plug capable driver) */
  	unsigned long driver_data;
+
+	struct device_driver driver;
  };

-struct vio_bus;
+static inline struct vio_driver *to_vio_driver(struct device_driver
*drv)
+{
+	return container_of(drv, struct vio_driver, driver);
+}
+
  /*
   * The vio_dev structure is used to describe virtual I/O devices.
   */
  struct vio_dev {
-	struct list_head devices_list;   /* node in list of all vio devices */
-	struct device_node *archdata;    /* Open Firmware node */
-	struct vio_bus *bus;            /* bus this device is on */
-	struct vio_driver *driver;      /* owning driver */
+	struct device_node *archdata;   /* Open Firmware node */
  	void *driver_data;              /* data private to the driver */
  	unsigned long unit_address;
-
-	struct TceTable *tce_table; /* vio_map_* uses this */
+	struct TceTable *tce_table;     /* vio_map_* uses this */
  	unsigned int irq;
-	struct proc_dir_entry *procent; /* device entry in /proc/bus/vio */
-};

-struct vio_bus {
-	struct list_head devices;       /* list of virtual devices */
+	struct device device;
  };

+static inline struct vio_dev *to_vio_dev(struct device *dev)
+{
+	return container_of(dev, struct vio_dev, device);
+}

+/* taken from pci_module_init() */
  static inline int vio_module_init(struct vio_driver *drv)
  {
-        int rc = vio_register_driver (drv);
+	int rc = vio_register_driver(drv);

-        if (rc > 0)
-                return 0;
+	if (rc > 0)
+		return 0;

-        /* iff CONFIG_HOTPLUG and built into kernel, we should
-         * leave the driver around for future hotplug events.
-         * For the module case, a hotplug daemon of some sort
-         * should load a module in response to an insert event. */
+	/* iff CONFIG_HOTPLUG and built into kernel, we should
+	 * leave the driver around for future hotplug events.
+	 * For the module case, a hotplug daemon of some sort
+	 * should load a module in response to an insert event. */
  #if defined(CONFIG_HOTPLUG) && !defined(MODULE)
-        if (rc == 0)
-                return 0;
+	if (rc == 0)
+		return 0;
  #else
-        if (rc == 0)
-                rc = -ENODEV;
+	if (rc == 0)
+		rc = -ENODEV;
  #endif

-        /* if we get here, we need to clean up vio driver instance
-         * and return some sort of error */
+	/* if we get here, we need to clean up vio driver instance
+	 * and return some sort of error */
+	vio_unregister_driver(drv);

-        return rc;
+	return rc;
  }

  #endif /* _PHYP_H */


More information about the Linuxppc64-dev mailing list