#ifndef MODULE_LIFECYCLE_H_ #define MODULE_LIFECYCLE_H_ #include #include #include enum module_lifecycle { LC_UNINIT, LC_STOPPED, LC_RUNNING, LC_ERROR, }; enum module_lifecycle_mode { ML_MODE_NONE, ML_MODE_POWER, ML_MODE_SUSPEND, }; struct module_lifecycle_ops { int (*do_init)(void); int (*do_start)(void); int (*do_stop)(void); }; struct module_lifecycle_cfg { enum module_lifecycle_mode mode; enum module_state stopped_state; }; struct module_lifecycle_ctx { enum module_lifecycle state; const struct module_lifecycle_cfg *cfg; const struct module_lifecycle_ops *ops; }; static inline bool module_lifecycle_is_running( const struct module_lifecycle_ctx *ctx) { return ctx->state == LC_RUNNING; } static inline bool module_lifecycle_is_initialized( const struct module_lifecycle_ctx *ctx) { return (ctx->state != LC_UNINIT) && (ctx->state != LC_ERROR); } static inline const char *module_lifecycle_name(enum module_lifecycle state) { switch (state) { case LC_UNINIT: return "UNINIT"; case LC_STOPPED: return "STOPPED"; case LC_RUNNING: return "RUNNING"; case LC_ERROR: return "ERROR"; default: return "?"; } } static inline int module_lifecycle_validate_ops( const struct module_lifecycle_ctx *ctx) { if ((ctx == NULL) || (ctx->cfg == NULL) || (ctx->ops == NULL) || (ctx->ops->do_init == NULL)) { return -EINVAL; } switch (ctx->cfg->mode) { case ML_MODE_NONE: return 0; case ML_MODE_POWER: case ML_MODE_SUSPEND: return (ctx->ops->do_start != NULL) && (ctx->ops->do_stop != NULL) ? 0 : -EINVAL; default: return -EINVAL; } } static inline int module_lifecycle_report_state( struct module_lifecycle_ctx *ctx, enum module_lifecycle state) { switch (state) { case LC_RUNNING: module_set_state(MODULE_STATE_READY); return 0; case LC_STOPPED: switch (ctx->cfg->mode) { case ML_MODE_POWER: module_set_state(ctx->cfg->stopped_state); return 0; case ML_MODE_SUSPEND: module_set_state(MODULE_STATE_SUSPENDED); return 0; case ML_MODE_NONE: return 0; default: return -EINVAL; } case LC_ERROR: module_set_state(MODULE_STATE_ERROR); return 0; case LC_UNINIT: default: return -EINVAL; } } static inline int module_lifecycle_fail(struct module_lifecycle_ctx *ctx, int err) { ctx->state = LC_ERROR; (void)module_lifecycle_report_state(ctx, LC_ERROR); return err ? err : -EIO; } static inline int module_set_lifecycle(struct module_lifecycle_ctx *ctx, enum module_lifecycle target) { int err; err = module_lifecycle_validate_ops(ctx); if (err) { return err; } if (ctx->state == LC_ERROR) { return -EFAULT; } if (ctx->state == target) { return 0; } if (target == LC_ERROR) { return module_lifecycle_fail(ctx, -EIO); } if ((target != LC_STOPPED) && (target != LC_RUNNING)) { return -EPERM; } switch (ctx->state) { case LC_UNINIT: err = ctx->ops->do_init(); if (err) { return module_lifecycle_fail(ctx, err); } ctx->state = LC_STOPPED; err = module_lifecycle_report_state(ctx, LC_STOPPED); if (err) { return module_lifecycle_fail(ctx, err); } return module_set_lifecycle(ctx, target); case LC_STOPPED: if (target != LC_RUNNING) { return -EPERM; } err = ctx->ops->do_start ? ctx->ops->do_start() : 0; if (err) { return module_lifecycle_fail(ctx, err); } ctx->state = LC_RUNNING; return module_lifecycle_report_state(ctx, LC_RUNNING); case LC_RUNNING: if (target != LC_STOPPED) { return -EPERM; } err = ctx->ops->do_stop ? ctx->ops->do_stop() : 0; if (err) { return module_lifecycle_fail(ctx, err); } ctx->state = LC_STOPPED; return module_lifecycle_report_state(ctx, LC_STOPPED); case LC_ERROR: default: return -EPERM; } } #endif /* MODULE_LIFECYCLE_H_ */