diff options
author | Corey Tabaka <corey.tabaka@gmail.com> | 2013-05-25 16:14:50 -0700 |
---|---|---|
committer | Corey Tabaka <corey.tabaka@gmail.com> | 2013-05-25 20:39:59 -0700 |
commit | 8bb850815881f209292f81222249540b6a6f8f45 (patch) | |
tree | 1f73236e414dea8568c45e5d87960d4d26d86997 | |
parent | a760678e556129f1107b62beb9f60a8dc765a390 (diff) | |
download | lk-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.c | 132 | ||||
-rw-r--r-- | dev/rules.mk | 3 | ||||
-rw-r--r-- | include/compiler.h | 4 | ||||
-rw-r--r-- | include/dev/driver.h | 113 | ||||
-rw-r--r-- | include/err.h | 1 |
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 |