[gPXE-devel] [PATCH 15/33] [linux] Add device and driver model

Piotr Jaroszyński p.jaroszynski at gmail.com
Sun Aug 15 18:59:20 EDT 2010


Add the base to build linux drivers and the linux UI code on.
UI fills device requests, which are later walked over by the linux
root_driver and delegated to specific linux drivers.

Signed-off-by: Piotr Jaroszyński <p.jaroszynski at gmail.com>
---
 src/Makefile               |    1 +
 src/drivers/linux/linux.c  |  152 ++++++++++++++++++++++++++++++++++++++++++++
 src/include/gpxe/errfile.h |    1 +
 src/include/gpxe/linux.h   |  144 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 298 insertions(+), 0 deletions(-)
 create mode 100644 src/drivers/linux/linux.c
 create mode 100644 src/include/gpxe/linux.h

diff --git a/src/Makefile b/src/Makefile
index df99680..a30932d 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -72,6 +72,7 @@ SRCDIRS		+= drivers/block
 SRCDIRS		+= drivers/nvs
 SRCDIRS		+= drivers/bitbash
 SRCDIRS		+= drivers/infiniband
+SRCDIRS		+= drivers/linux
 SRCDIRS		+= interface/pxe interface/efi interface/smbios interface/linux
 SRCDIRS		+= tests
 SRCDIRS		+= crypto crypto/axtls crypto/matrixssl
diff --git a/src/drivers/linux/linux.c b/src/drivers/linux/linux.c
new file mode 100644
index 0000000..43d3e6b
--- /dev/null
+++ b/src/drivers/linux/linux.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 Piotr Jaroszyński <p.jaroszynski at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+FILE_LICENCE(GPL2_OR_LATER);
+
+/** @file
+ *
+ * Linux root_device and root_driver.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <gpxe/linux.h>
+#include <gpxe/malloc.h>
+#include <gpxe/settings.h>
+
+LIST_HEAD(linux_device_requests);
+LIST_HEAD(linux_global_settings);
+
+/** Go over the device requests looking for a matching linux driver to handle them. */
+static int linux_probe(struct root_device *rootdev)
+{
+	struct linux_device_request *request;
+	struct linux_driver *driver;
+	struct linux_device *device = NULL;
+	int rc;
+
+	/* Apply global settings */
+	linux_apply_settings(&linux_global_settings, NULL);
+
+	list_for_each_entry(request, &linux_device_requests, list) {
+		if (! device)
+			device = zalloc(sizeof(*device));
+
+		if (! device)
+			return -ENOMEM;
+
+		rc = 1;
+
+		for_each_table_entry(driver, LINUX_DRIVERS) {
+			if ((rc = strcmp(driver->name, request->driver)) == 0)
+				break;
+		}
+
+		if (rc != 0) {
+			printf("Linux driver '%s' not found\n", request->driver);
+			continue;
+		}
+
+		if (! driver->can_probe) {
+			printf("Driver '%s' cannot handle any more devices\n", driver->name);
+			continue;
+		}
+
+		/* We found a matching driver so add the device to the hierarchy */
+		list_add(&device->dev.siblings, &rootdev->dev.children);
+		device->dev.parent = &rootdev->dev;
+		INIT_LIST_HEAD(&device->dev.children);
+
+		if (driver->probe(device, request) == 0) {
+			device->driver = driver;
+			/* Driver handled the device so release ownership */
+			device = NULL;
+		} else {
+			/* Driver failed to handle the device so remove it from the hierarchy
+			 * and reuse the object */
+			list_del(&device->dev.siblings);
+		}
+	};
+
+	free(device);
+
+	return 0;
+}
+
+/** Remove all the linux devices registered in probe() */
+static void linux_remove(struct root_device *rootdev)
+{
+	struct linux_device *device;
+	struct linux_device *tmp;
+
+	list_for_each_entry_safe(device, tmp, &rootdev->dev.children, dev.siblings) {
+		list_del(&device->dev.siblings);
+		device->driver->remove(device);
+		free(device);
+	}
+}
+
+/** Linux root driver */
+static struct root_driver linux_root_driver = {
+	.probe = linux_probe,
+	.remove = linux_remove,
+};
+
+/** Linux root device */
+struct root_device linux_root_device __root_device = {
+	.dev = { .name = "linux" },
+	.driver = &linux_root_driver,
+};
+
+struct linux_setting *linux_find_setting(char *name, struct list_head *settings)
+{
+	struct linux_setting *setting;
+	struct linux_setting *result = NULL;
+
+	/* Find the last occurrence of a setting with the specified name */
+	list_for_each_entry(setting, settings, list) {
+		if (strcmp(setting->name, name) == 0) {
+			result = setting;
+		}
+	}
+
+	return result;
+}
+
+void linux_apply_settings(struct list_head *new_settings, struct settings *settings_block)
+{
+	struct linux_setting *setting;
+	int rc;
+
+	list_for_each_entry(setting, new_settings, list) {
+		/* Skip already applied settings */
+		if (setting->applied)
+			continue;
+
+		struct setting *s = find_setting(setting->name);
+		if (s) {
+			rc = storef_setting(settings_block, find_setting(setting->name), setting->value);
+			if (rc != 0)
+				DBG("linux storing setting '%s' = '%s' failed\n", setting->name, setting->value);
+			setting->applied = 1;
+		} else {
+			DBG("linux unknown setting '%s'\n", setting->name);
+		}
+	}
+}
diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h
index cc1d956..7267439 100644
--- a/src/include/gpxe/errfile.h
+++ b/src/include/gpxe/errfile.h
@@ -61,6 +61,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define ERRFILE_isapnp		     ( ERRFILE_DRIVER | 0x00020000 )
 #define ERRFILE_mca		     ( ERRFILE_DRIVER | 0x00030000 )
 #define ERRFILE_pci		     ( ERRFILE_DRIVER | 0x00040000 )
+#define ERRFILE_linux		     ( ERRFILE_DRIVER | 0x00050000 )
 
 #define ERRFILE_nvs		     ( ERRFILE_DRIVER | 0x00100000 )
 #define ERRFILE_spi		     ( ERRFILE_DRIVER | 0x00110000 )
diff --git a/src/include/gpxe/linux.h b/src/include/gpxe/linux.h
new file mode 100644
index 0000000..a03df6b
--- /dev/null
+++ b/src/include/gpxe/linux.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2010 Piotr Jaroszyński <p.jaroszynski at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _GPXE_LINUX_H
+#define _GPXE_LINUX_H
+
+FILE_LICENCE(GPL2_OR_LATER);
+
+/** @file
+ *
+ * Linux devices, drivers and device requests.
+ */
+
+#include <gpxe/list.h>
+#include <gpxe/device.h>
+#include <gpxe/settings.h>
+
+/** A linux device */
+struct linux_device {
+	/** Generic device */
+	struct device dev;
+	/** Driver that's handling the device */
+	struct linux_driver *driver;
+	/** Private data used by drivers */
+	void *priv;
+};
+
+struct linux_device_request;
+
+/** A linux driver */
+struct linux_driver {
+	/** Name */
+	char *name;
+	/** Probe function */
+	int (*probe)(struct linux_device *device, struct linux_device_request *request);
+	/** Remove function */
+	void (*remove)(struct linux_device *device);
+	/** Can the driver probe any more devices? */
+	int can_probe;
+};
+
+/** Linux driver table */
+#define LINUX_DRIVERS __table(struct linux_driver, "linux_drivers")
+
+/** Declare a Linux driver */
+#define __linux_driver __table_entry(LINUX_DRIVERS, 01)
+
+/**
+ * Set linux device driver-private data
+ *
+ * @v device	Linux device
+ * @v priv		Private data
+ */
+static inline void linux_set_drvdata(struct linux_device * device, void *priv)
+{
+	device->priv = priv;
+}
+
+/**
+ * Get linux device driver-private data
+ *
+ * @v device	Linux device
+ * @ret priv	Private data
+ */
+static inline void *linux_get_drvdata(struct linux_device *device)
+{
+	return device->priv;
+}
+
+/**
+ * A device request.
+ *
+ * To be created and filled by the UI code.
+ */
+struct linux_device_request {
+	/** Driver name. Compared to the linux drivers' names */
+	char *driver;
+	/** List node */
+	struct list_head list;
+	/** List of settings */
+	struct list_head settings;
+};
+
+/** A device request setting */
+struct linux_setting {
+	/** Name */
+	char *name;
+	/** Value */
+	char *value;
+	/** Was the setting already applied? */
+	int applied;
+	/** List node */
+	struct list_head list;
+};
+
+/**
+ * List of requested devices.
+ *
+ * Filled by the UI code. Linux root_driver walks over this list looking for an
+ * appropriate driver to handle each request by matching the driver's name.
+ */
+extern struct list_head linux_device_requests;
+
+/**
+ * List of global settings to apply.
+ *
+ * Filled by the UI code. Linux root_driver applies these settings.
+ */
+extern struct list_head linux_global_settings;
+
+/**
+ * Look for the last occurrence of a setting with the specified name
+ *
+ * @v name     Name of the setting to look for
+ * @v settings List of the settings to look through
+ */
+struct linux_setting *linux_find_setting(char *name, struct list_head *settings);
+
+/**
+ * Apply a list of linux settings to a settings block
+ *
+ * @v new_settings     List of linux_setting's to apply
+ * @v settings_block   Settings block to apply the settings to
+ * @ret rc             0 on success
+ */
+extern void linux_apply_settings(struct list_head *new_settings, struct settings *settings_block);
+
+
+#endif /* _GPXE_LINUX_H */
-- 
1.7.1



More information about the gPXE-devel mailing list