summaryrefslogtreecommitdiffstats
path: root/common/state/backend_bucket_cached.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/state/backend_bucket_cached.c')
-rw-r--r--common/state/backend_bucket_cached.c155
1 files changed, 155 insertions, 0 deletions
diff --git a/common/state/backend_bucket_cached.c b/common/state/backend_bucket_cached.c
new file mode 100644
index 0000000..781ac2d
--- /dev/null
+++ b/common/state/backend_bucket_cached.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <common.h>
+#include "state.h"
+
+struct state_backend_storage_bucket_cache {
+ struct state_backend_storage_bucket bucket;
+
+ struct state_backend_storage_bucket *raw;
+
+ u8 *data;
+ ssize_t data_len;
+ bool force_write;
+
+ /* For outputs */
+ struct device_d *dev;
+};
+
+static inline struct state_backend_storage_bucket_cache
+ *get_bucket_cache(struct state_backend_storage_bucket *bucket)
+{
+ return container_of(bucket,
+ struct state_backend_storage_bucket_cache,
+ bucket);
+}
+
+static inline void state_backend_bucket_cache_drop(
+ struct state_backend_storage_bucket_cache *cache)
+{
+ if (cache->data) {
+ free(cache->data);
+ cache->data = NULL;
+ cache->data_len = 0;
+ }
+}
+
+static int state_backend_bucket_cache_fill(
+ struct state_backend_storage_bucket_cache *cache)
+{
+ int ret;
+
+ ret = cache->raw->read(cache->raw, &cache->data, &cache->data_len);
+ if (ret == -EUCLEAN)
+ cache->force_write = true;
+ else if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int state_backend_bucket_cache_read(struct state_backend_storage_bucket *bucket,
+ uint8_t ** buf_out,
+ ssize_t * len_hint)
+{
+ struct state_backend_storage_bucket_cache *cache =
+ get_bucket_cache(bucket);
+ int ret;
+
+ if (!cache->data) {
+ ret = state_backend_bucket_cache_fill(cache);
+ if (ret)
+ return ret;
+ }
+
+ if (cache->data) {
+ *buf_out = xmemdup(cache->data, cache->data_len);
+ if (!*buf_out)
+ return -ENOMEM;
+ *len_hint = cache->data_len;
+ }
+
+ return 0;
+}
+
+static int state_backend_bucket_cache_write(struct state_backend_storage_bucket *bucket,
+ const uint8_t * buf, ssize_t len)
+{
+ struct state_backend_storage_bucket_cache *cache =
+ get_bucket_cache(bucket);
+ int ret;
+
+ if (!cache->force_write) {
+ if (!cache->data)
+ ret = state_backend_bucket_cache_fill(cache);
+
+ if (cache->data_len == len && !memcmp(cache->data, buf, len))
+ return 0;
+ }
+
+ state_backend_bucket_cache_drop(cache);
+
+ ret = cache->raw->write(cache->raw, buf, len);
+ if (ret)
+ return ret;
+
+ cache->data = xmemdup(buf, len);
+ cache->data_len = len;
+ return 0;
+}
+
+static int state_backend_bucket_cache_init(
+ struct state_backend_storage_bucket *bucket)
+{
+ struct state_backend_storage_bucket_cache *cache =
+ get_bucket_cache(bucket);
+
+ if (cache->raw->init) {
+ return cache->raw->init(cache->raw);
+ }
+
+ return 0;
+}
+
+static void state_backend_bucket_cache_free(
+ struct state_backend_storage_bucket *bucket)
+{
+ struct state_backend_storage_bucket_cache *cache =
+ get_bucket_cache(bucket);
+
+ state_backend_bucket_cache_drop(cache);
+ cache->raw->free(cache->raw);
+ free(cache);
+}
+
+int state_backend_bucket_cached_create(struct device_d *dev,
+ struct state_backend_storage_bucket *raw,
+ struct state_backend_storage_bucket **out)
+{
+ struct state_backend_storage_bucket_cache *cache;
+
+ cache = xzalloc(sizeof(*cache));
+ cache->raw = raw;
+ cache->dev = dev;
+
+ cache->bucket.free = state_backend_bucket_cache_free;
+ cache->bucket.read = state_backend_bucket_cache_read;
+ cache->bucket.write = state_backend_bucket_cache_write;
+ cache->bucket.init = state_backend_bucket_cache_init;
+
+ *out = &cache->bucket;
+
+ return 0;
+}