aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorey Tabaka <corey.tabaka@gmail.com>2013-05-25 16:14:50 -0700
committerCorey Tabaka <corey.tabaka@gmail.com>2013-05-25 20:39:59 -0700
commit8bb850815881f209292f81222249540b6a6f8f45 (patch)
tree1f73236e414dea8568c45e5d87960d4d26d86997
parenta760678e556129f1107b62beb9f60a8dc765a390 (diff)
downloadlk-8bb850815881f209292f81222249540b6a6f8f45.tar.gz
[dev] Add simple multi-instance device driver model.
Model supports class drivers capable of driving multiple device instances independently. Implements simple pattern for device life cycle management. Completely optional and self-contained.
-rw-r--r--dev/driver.c132
-rw-r--r--dev/rules.mk3
-rw-r--r--include/compiler.h4
-rw-r--r--include/dev/driver.h113
-rw-r--r--include/err.h1
5 files changed, 252 insertions, 1 deletions
diff --git a/dev/driver.c b/dev/driver.c
new file mode 100644
index 00000000..e3a07c32
--- /dev/null
+++ b/dev/driver.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2012 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (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 <dev/driver.h>
+#include <assert.h>
+#include <err.h>
+
+extern struct device __devices[];
+extern struct device __devices_end[];
+
+status_t device_init_all(void)
+{
+ status_t res = NO_ERROR;
+
+ struct device *dev = __devices;
+ while (dev != __devices_end) {
+ status_t code = device_init(dev);
+
+ if (code < 0) {
+ TRACEF("Driver init failed for driver \"%s\", device \"%s\", reason %d\n",
+ dev->driver->type, dev->name, code);
+
+ res = code;
+ }
+
+ dev++;
+ }
+
+ return res;
+}
+
+status_t device_fini_all(void)
+{
+ status_t res = NO_ERROR;
+
+ struct device *dev = __devices;
+ while (dev != __devices_end) {
+ status_t code = device_fini(dev);
+
+ if (code < 0) {
+ TRACEF("Driver fini failed for driver \"%s\", device \"%s\", reason %d\n",
+ dev->driver->type, dev->name, code);
+
+ res = code;
+ }
+
+ dev++;
+ }
+
+ return res;
+}
+
+status_t device_init(struct device *dev)
+{
+ if (!dev)
+ return ERR_INVALID_ARGS;
+
+ DEBUG_ASSERT(dev->driver);
+
+ const struct driver_ops *ops = dev->driver->ops;
+
+ if (ops && ops->init)
+ return ops->init(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t device_fini(struct device *dev)
+{
+ if (!dev)
+ return ERR_INVALID_ARGS;
+
+ DEBUG_ASSERT(dev->driver);
+
+ const struct driver_ops *ops = dev->driver->ops;
+
+ if (ops && ops->fini)
+ return ops->fini(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t device_suspend(struct device *dev)
+{
+ if (!dev)
+ return ERR_NOT_SUPPORTED;
+
+ DEBUG_ASSERT(dev->driver);
+
+ const struct driver_ops *ops = dev->driver->ops;
+
+ if (ops && ops->suspend)
+ return ops->suspend(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t device_resume(struct device *dev)
+{
+ if (!dev)
+ return ERR_NOT_SUPPORTED;
+
+ DEBUG_ASSERT(dev->driver);
+
+ const struct driver_ops *ops = dev->driver->ops;
+
+ if (ops && ops->resume)
+ return ops->resume(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
diff --git a/dev/rules.mk b/dev/rules.mk
index a254e3e1..06b64588 100644
--- a/dev/rules.mk
+++ b/dev/rules.mk
@@ -3,6 +3,7 @@ LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
- $(LOCAL_DIR)/dev.c
+ $(LOCAL_DIR)/dev.c \
+ $(LOCAL_DIR)/driver.c \
include make/module.mk
diff --git a/include/compiler.h b/include/compiler.h
index 0284d0de..a9a49176 100644
--- a/include/compiler.h
+++ b/include/compiler.h
@@ -151,6 +151,10 @@
/* TODO: add type check */
#define countof(a) (sizeof(a) / sizeof((a)[0]))
+/* macro-expanding concat */
+#define concat(a, b) __ex_concat(a, b)
+#define __ex_concat(a, b) a ## b
+
/* CPP header guards */
#ifdef __cplusplus
#define __BEGIN_CDECLS extern "C" {
diff --git a/include/dev/driver.h b/include/dev/driver.h
new file mode 100644
index 00000000..790317b5
--- /dev/null
+++ b/include/dev/driver.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2012 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (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.
+ */
+#ifndef __DEV_DRIVER_H
+#define __DEV_DRIVER_H
+
+#include <sys/types.h>
+#include <list.h> // for containerof
+#include <compiler.h>
+
+struct driver;
+
+/*
+ * Contains the data pertaining to an instance of a device. More than one
+ * instance may exist for a given driver type (i.e. uart0, uart1, etc..).
+ */
+struct device {
+ const char *name;
+ const struct driver *driver;
+
+ /* instance specific config data populated at instantiation */
+ const void *config;
+
+ /* instance specific data populated by the driver at init */
+ void *state;
+
+ // TODO: add generic state, such as suspend/resume state, etc...
+};
+
+/* device class, mainly used as a unique magic pointer to validate ops */
+struct device_class {
+ const char *name;
+};
+
+/* standard driver ops; extensions should contain this ops structure */
+struct driver_ops {
+ const struct device_class *device_class;
+
+ status_t (*init)(struct device *dev);
+ status_t (*fini)(struct device *dev);
+
+ status_t (*suspend)(struct device *dev);
+ status_t (*resume)(struct device *dev);
+};
+
+/* describes a driver, one per driver type */
+struct driver {
+ const char *type;
+ const struct driver_ops *ops;
+};
+
+#define DRIVER_EXPORT(type_, ops_) \
+ struct driver concat(__driver_, type_) \
+ __SECTION(".drivers") = { \
+ .type = #type_, \
+ .ops = ops_, \
+ }
+
+#define DEVICE_INSTANCE(type_, name_, config_) \
+ extern struct driver concat(__driver_, type_); \
+ struct device concat(__device_, concat(type_, concat(_, name_))) \
+ __SECTION(".devices") = { \
+ .name = #name_, \
+ .driver = &concat(__driver_, type_), \
+ .config = config_, \
+ }
+
+/*
+ * returns the driver specific ops pointer given the device instance, specifc
+ * ops type, and generic ops member name within the specific ops structure.
+ */
+#define device_get_driver_ops(dev, type, member) ({ \
+ type *__ops = NULL; \
+ if (dev && dev->driver && dev->driver->ops) \
+ __ops = containerof(dev->driver->ops, type, member); \
+ __ops; \
+})
+
+#define device_get_by_name(type_, name_) ({ \
+ extern struct device concat(__device_, concat(type_, concat(_, name_))); \
+ &concat(__device_, concat(type_, concat(_, name_))); \
+})
+
+status_t device_init_all(void);
+status_t device_fini_all(void);
+
+status_t device_init(struct device *dev);
+status_t device_fini(struct device *dev);
+
+status_t device_suspend(struct device *dev);
+status_t device_resume(struct device *dev);
+
+#endif
+
diff --git a/include/err.h b/include/err.h
index 3f148bb6..d1404b0d 100644
--- a/include/err.h
+++ b/include/err.h
@@ -52,5 +52,6 @@
#define ERR_NOT_SUPPORTED -24
#define ERR_TOO_BIG -25
#define ERR_THREAD_DETACHED -26
+#define ERR_NOT_CONFIGURED -27
#endif