+ struct shash_node *node;
+ SHASH_FOR_EACH(node, &netdev_classes) {
+ const struct netdev_class *netdev_class = node->data;
+ if (netdev_class->wait) {
+ netdev_class->wait();
+ }
+ }
+}
+
+/* Initializes and registers a new netdev provider. After successful
+ * registration, new netdevs of that type can be opened using netdev_open(). */
+int
+netdev_register_provider(const struct netdev_class *new_class)
+{
+ struct netdev_class *new_provider;
+
+ if (shash_find(&netdev_classes, new_class->type)) {
+ VLOG_WARN("attempted to register duplicate netdev provider: %s",
+ new_class->type);
+ return EEXIST;
+ }
+
+ if (new_class->init) {
+ int error = new_class->init();
+ if (error) {
+ VLOG_ERR("failed to initialize %s network device class: %s",
+ new_class->type, strerror(error));
+ return error;
+ }
+ }
+
+ new_provider = xmalloc(sizeof *new_provider);
+ memcpy(new_provider, new_class, sizeof *new_provider);
+
+ shash_add(&netdev_classes, new_class->type, new_provider);
+
+ return 0;
+}
+
+/* Unregisters a netdev provider. 'type' must have been previously
+ * registered and not currently be in use by any netdevs. After unregistration
+ * new netdevs of that type cannot be opened using netdev_open(). */
+int
+netdev_unregister_provider(const char *type)
+{
+ struct shash_node *del_node, *netdev_dev_node;
+
+ del_node = shash_find(&netdev_classes, type);
+ if (!del_node) {
+ VLOG_WARN("attempted to unregister a netdev provider that is not "
+ "registered: %s", type);
+ return EAFNOSUPPORT;
+ }
+
+ SHASH_FOR_EACH(netdev_dev_node, &netdev_dev_shash) {
+ struct netdev_dev *netdev_dev = netdev_dev_node->data;
+ if (!strcmp(netdev_dev->netdev_class->type, type)) {
+ VLOG_WARN("attempted to unregister in use netdev provider: %s",
+ type);
+ return EBUSY;
+ }
+ }
+
+ shash_delete(&netdev_classes, del_node);
+ free(del_node->data);
+
+ return 0;
+}
+
+/* Clears 'types' and enumerates the types of all currently registered netdev
+ * providers into it. The caller must first initialize the svec. */
+void
+netdev_enumerate_types(struct svec *types)
+{
+ struct shash_node *node;
+
+ netdev_initialize();
+ svec_clear(types);
+
+ SHASH_FOR_EACH(node, &netdev_classes) {
+ const struct netdev_class *netdev_class = node->data;
+ svec_add(types, netdev_class->type);
+ }
+}
+
+/* Compares 'args' to those used to those used by 'dev'. Returns true
+ * if the arguments are the same, false otherwise. Does not update the
+ * values stored in 'dev'. */
+static bool
+compare_device_args(const struct netdev_dev *dev, const struct shash *args)
+{
+ const struct shash_node **new_args;
+ bool result = true;
+ int i;
+
+ if (shash_count(args) != dev->n_args) {
+ return false;
+ }
+
+ new_args = shash_sort(args);
+ for (i = 0; i < dev->n_args; i++) {
+ if (strcmp(dev->args[i].key, new_args[i]->name) ||
+ strcmp(dev->args[i].value, new_args[i]->data)) {
+ result = false;
+ goto finish;
+ }
+ }
+
+finish:
+ free(new_args);
+ return result;
+}
+
+static int
+compare_args(const void *a_, const void *b_)
+{
+ const struct arg *a = a_;
+ const struct arg *b = b_;
+ return strcmp(a->key, b->key);
+}
+
+void
+update_device_args(struct netdev_dev *dev, const struct shash *args)
+{
+ struct shash_node *node;