From c6546d078529ef8fb4eba29bdc135ea8fcfead40 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michal=20Mal=C3=BD?= Date: Fri, 25 Jul 2014 20:56:15 +0200 Subject: [PATCH 1/1] Initial commit --- Makefile | 14 ++++ klgd.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ klgd.h | 29 ++++++++ 3 files changed, 261 insertions(+) create mode 100644 Makefile create mode 100644 klgd.c create mode 100644 klgd.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..33f4730 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +ifneq ($(KERNELRELEASE),) + obj-m += klgd.o + +else + KERNELDIR ?= /lib/modules/$(shell uname -r)/build + PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) + +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean + +endif \ No newline at end of file diff --git a/klgd.c b/klgd.c new file mode 100644 index 0000000..feb2484 --- /dev/null +++ b/klgd.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include "klgd.h" + +struct klgd_main_private { + atomic_t can_send_commands; + void *device_context; + unsigned long earliest_update; + struct klgd_command_stream *last_stream; + size_t plugin_count; + struct klgd_plugin **plugins; + atomic_t send_asap; + spinlock_t stream_lock; + struct timer_list timer; + + int (*send_command_stream)(void *dev_ctx, struct klgd_command_stream *stream); +}; + +void klgd_free_stream(struct klgd_command_stream *s); +void klgd_timer_fired(unsigned long ctx); +void klgd_schedule_update(struct klgd_main *ctx); + +bool klgd_append_stream(struct klgd_command_stream *target, struct klgd_command_stream *source) +{ + struct klgd_command **temp; + size_t idx; + + if (!source->count) + return true; + + temp = krealloc(target->commands, sizeof(struct klgd_command *) * (target->count + source->count), GFP_ATOMIC); + if (!temp) + return false; + + target->commands = temp; + for (idx = 0; idx < source->count; idx++) + target->commands[idx + target->count] = source->commands[idx]; + target->count += source->count; + + return true; +} + +void klgd_build_command_stream(struct klgd_main *ctx) +{ + struct klgd_main_private *priv = ctx->private; + const unsigned long now = jiffies; + size_t idx; + + struct klgd_command_stream *s = kzalloc(sizeof(struct klgd_command_stream), GFP_ATOMIC); + if (!s) + return; /* FIXME: Try to do an update later when some memory might be available */ + + for (idx = 0; idx < priv->plugin_count; idx++) { + struct klgd_plugin *plugin = priv->plugins[idx]; + struct klgd_command_stream *ss = plugin->get_commands(plugin, now); + /* FIXME: Same as above */ + if (!klgd_append_stream(s, ss)) { + klgd_free_stream(s); + return; + } + } + + if (s->count) { + atomic_set(&priv->can_send_commands, 0); + priv->last_stream = s; + priv->send_command_stream(priv->device_context, s); + } +} + +void klgd_free_stream(struct klgd_command_stream *s) +{ + size_t idx; + + if (!s) + return; + + for (idx = 0; idx < s->count; idx++) { + kfree(s->commands[idx]->bytes); + kfree(s->commands[idx]); + } +} + +int klgd_init(struct klgd_main *ctx, void *dev_ctx, int (*callback)(void *, struct klgd_command_stream *), const size_t plugin_count) +{ + struct klgd_main_private *priv = ctx->private; + int ret; + + if (!ctx) + return -EINVAL; + if (plugin_count < 1) + return -EINVAL; + if (!callback) + return -EINVAL; + + priv = kzalloc(sizeof(struct klgd_main_private), GFP_KERNEL); + if (!ctx->private) + return -ENOMEM; + + priv->plugins = kzalloc(sizeof(struct klgd_plugin *) * plugin_count, GFP_KERNEL); + if (!ctx->private->plugins) { + ret = -ENOMEM; + goto err_out; + } + priv->plugin_count = plugin_count; + + atomic_set(&priv->can_send_commands, 1); + priv->device_context = dev_ctx; + priv->last_stream = NULL; + spin_lock_init(&priv->stream_lock); + priv->send_command_stream = callback; + atomic_set(&priv->send_asap, 0); + + setup_timer(&priv->timer, klgd_timer_fired, (unsigned long)ctx); + + return 0; + +err_out: + kfree(ctx->private); + + return ret; +} + +void klgd_notify_commands_sent(struct klgd_main *ctx) +{ + struct klgd_main_private *priv = ctx->private; + + kfree(priv->last_stream); + + if (atomic_read(&priv->send_asap)) { + unsigned long flags; + + spin_lock_irqsave(&priv->stream_lock, flags); + klgd_build_command_stream(ctx); + spin_unlock_irqrestore(&priv->stream_lock, flags); + atomic_set(&priv->send_asap, 0); + } else + atomic_set(&priv->can_send_commands, 1); +} + +int klgd_post_event(struct klgd_main *ctx, size_t idx, void *data) +{ + struct klgd_plugin *plugin = ctx->private->plugins[idx]; + int ret; + + ret = plugin->post_event(plugin, data); + if (ret) + return ret; + + klgd_schedule_update(ctx); + return 0; +} + +int klgd_register_plugin(struct klgd_main *ctx, size_t idx, struct klgd_plugin *plugin) +{ + struct klgd_main_private *priv = ctx->private; + + if (priv->plugins[idx]) + return -EINVAL; + + priv->plugins[idx] = kzalloc(sizeof(struct klgd_plugin), GFP_KERNEL); + if (!priv->plugins[idx]) + return -ENOMEM; + + priv->plugins[idx] = plugin; + return 0; +} + +void klgd_schedule_update(struct klgd_main *ctx) +{ + struct klgd_main_private *priv = ctx->private; + const unsigned long now = jiffies; + unsigned int events = 0; + unsigned long earliest; + size_t idx; + + for (idx = 0; idx < priv->plugin_count; idx++) { + struct klgd_plugin *plugin = priv->plugins[idx]; + unsigned long t; + + if (plugin->get_update_time(plugin, now, &t)) { + if (!events) + earliest = t; + else { + if (time_before(t, earliest)) + earliest = t; + } + events++; + } + } + + if (!events) { + pr_debug("No events, deactivating timer\n"); + del_timer(&priv->timer); + } else { + pr_debug("Events: %u, earliest: %lu, now: %lu\n", events, earliest, now); + mod_timer(&priv->timer, earliest); + } +} + +void klgd_timer_fired(unsigned long ctx) +{ + struct klgd_main *m = (struct klgd_main *)ctx; + struct klgd_main_private *priv = m->private; + + + if (atomic_read(&priv->can_send_commands)) { + unsigned long flags; + + spin_lock_irqsave(&priv->stream_lock, flags); + klgd_build_command_stream(m); + spin_unlock_irqrestore(&priv->stream_lock, flags); + } else + atomic_set(&priv->send_asap, 1); + + klgd_schedule_update(ctx); +} \ No newline at end of file diff --git a/klgd.h b/klgd.h new file mode 100644 index 0000000..7512099 --- /dev/null +++ b/klgd.h @@ -0,0 +1,29 @@ +struct klgd_command { + const char *bytes; + size_t length; +}; + +struct klgd_command_stream { + struct klgd_command **commands; + size_t count; +}; + +struct klgd_main { + struct klgd_main_private *private; +}; + +struct klgd_plugin { + struct klgd_plugin_private *private; + + void (*deinit)(struct klgd_plugin *ctx, void *data); + struct klgd_command_stream *(*get_commands)(struct klgd_plugin *ctx, const unsigned long now); + bool (*get_update_time)(struct klgd_plugin *ctx, const unsigned long now, unsigned long *t); + int (*init)(struct klgd_plugin *ctx, void *data); + bool (*needs_attention)(struct klgd_plugin *ctx); + int (*post_event)(struct klgd_plugin *ctx, void *data); +}; + +void klgd_deinit(struct klgd_main *ctx, void *data); +int klgd_init(struct klgd_main *ctx, void *dev_ctx, int (*callback)(void *, struct klgd_command_stream *), const unsigned long plugin_count); +int klgd_post_event(struct klgd_main *ctx, const size_t idx, void *data); +int klgd_register_plugin(struct klgd_main *ctx, const size_t idx, struct klgd_plugin *plugin); -- 2.43.5