aboutsummaryrefslogtreecommitdiff
path: root/lib/drivers
diff options
context:
space:
mode:
authorAndy Green <andy@warmcat.com>2020-06-10 19:17:08 +0100
committerAndy Green <andy@warmcat.com>2020-06-10 19:17:08 +0100
commit15ce46d9716dfeb70ff4f6d25225bf78f1443a5b (patch)
tree6a687d780f8c39aad2724b30fb81d74ad8acbf18 /lib/drivers
parentad5a4f7040615c33fc5ab0903bf66ec0376510b9 (diff)
downloadlibwebsockets-15ce46d9716dfeb70ff4f6d25225bf78f1443a5b.tar.gz
drivers: initial generic gpio and i2c plus bitbang
Make a start on generic peripheral and bus drivers to provide meta-functionality regardless of platform. On the one hand this simply provides... - bitbang i2c on top of esp-idf gpio apis - ssd1306 oled chip driver as found on Heltec WB32 - modifications to the minimal example test for esp32 to use that ... on the other hand, those capabilities are provided by creating: - an abstract i2c class object - an abstract gpio class object - i2c class implementation using the abstract gpio for bitbang - an abstract display class object - an abstract display state (brightness, animated change, on/off/init tracking, autodim after inactive, auto-off / blanking after inactive) ... with the intention, eg, you only have to add a platform implementation for the gpio to be able to use the i2c-based display drivers and state handling, and i2c bitbang, without any other modifications.
Diffstat (limited to 'lib/drivers')
-rw-r--r--lib/drivers/CMakeLists.txt14
-rw-r--r--lib/drivers/devices/display/ssd1306.h66
-rw-r--r--lib/drivers/display/lws-display.c126
-rw-r--r--lib/drivers/display/ssd1306-i2c.c151
-rw-r--r--lib/drivers/i2c/bitbang/lws-bb-i2c.c135
-rw-r--r--lib/drivers/i2c/lws-i2c.c59
6 files changed, 551 insertions, 0 deletions
diff --git a/lib/drivers/CMakeLists.txt b/lib/drivers/CMakeLists.txt
new file mode 100644
index 00000000..33a6b193
--- /dev/null
+++ b/lib/drivers/CMakeLists.txt
@@ -0,0 +1,14 @@
+list(APPEND SOURCES
+ drivers/display/lws-display.c
+ drivers/display/ssd1306-i2c.c
+ drivers/i2c/lws-i2c.c
+ drivers/i2c/bitbang/lws-bb-i2c.c
+)
+
+if (LWS_ESP_PLATFORM)
+ list(APPEND SOURCES
+ plat/freertos/esp32/drivers/gpio-esp32.c)
+endif()
+
+exports_to_parent_scope()
+
diff --git a/lib/drivers/devices/display/ssd1306.h b/lib/drivers/devices/display/ssd1306.h
new file mode 100644
index 00000000..0d8fb459
--- /dev/null
+++ b/lib/drivers/devices/display/ssd1306.h
@@ -0,0 +1,66 @@
+/*
+ * Private register map for SSD1306
+ *
+ * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * 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.
+ *
+ */
+
+#if !defined(__LWS_SSD1306_H__)
+#define __LWS_SSD1306_H__
+
+enum {
+ SSD1306_SETLOWCOLUMN = 0x00,
+ SSD1306_SETHIGHCOLUMN = 0x10,
+
+ SSD1306_MEMORYMODE = 0x20,
+ SSD1306_COLUMNADDR = 0x21,
+ SSD1306_PAGEADDR = 0x22,
+ SSD1306_DEACTIVATE_SCROLL = 0x2e,
+
+ SSD1306_SETSTARTLINE = 0x40,
+
+ SSD1306_SETCONTRAST = 0x81,
+ SSD1306_CHARGEPUMP = 0x8d,
+
+ SSD1306_SEGREMAP = 0xa0,
+ SSD1306_SETSEGMENTREMAP = 0xa1,
+ SSD1306_DISPLAYALLON_RESUME = 0xa4,
+ SSD1306_DISPLAYALLON = 0xa5,
+ SSD1306_NORMALDISPLAY = 0xa6,
+ SSD1306_INVERTDISPLAY = 0xa7,
+ SSD1306_SETMULTIPLEX = 0xa8,
+ SSD1306_DISPLAYOFF = 0xae,
+ SSD1306_DISPLAYON = 0xaf,
+
+ SSD1306_COMSCANINC = 0xc0,
+ SSD1306_COMSCANDEC = 0xc8,
+
+ SSD1306_SETDISPLAYOFFSET = 0xd3,
+ SSD1306_SETDISPLAYCLOCKDIV = 0xd5,
+ SSD1306_SETPRECHARGE = 0xd9,
+ SSD1306_SETCOMPINS = 0xda,
+ SSD1306_SETVCOMDESELECT = 0xdb,
+
+ SSD1306_NOP = 0xe3,
+};
+
+#endif
+
diff --git a/lib/drivers/display/lws-display.c b/lib/drivers/display/lws-display.c
new file mode 100644
index 00000000..32e85368
--- /dev/null
+++ b/lib/drivers/display/lws-display.c
@@ -0,0 +1,126 @@
+/*
+ * lws abstract display
+ *
+ * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * 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 <libwebsockets.h>
+
+static void
+sul_cb(lws_sorted_usec_list_t *sul)
+{
+ lws_display_state_t *lds = lws_container_of(sul, lws_display_state_t,
+ sul);
+
+ if (lds->bl_target > lds->bl_current) {
+ if (lds->bl_target - lds->bl_current < lds->bl_step)
+ lds->bl_current = lds->bl_target;
+ else
+ lds->bl_current += lds->bl_step;
+ } else {
+ if (lds->bl_current - lds->bl_target < lds->bl_step)
+ lds->bl_current = lds->bl_target;
+ else
+ lds->bl_current -= lds->bl_step;
+ }
+
+ lds->disp->brightness(lds->disp, lds->bl_current);
+
+ if (lds->bl_current != lds->bl_target)
+ /*
+ * Come back and move towards the target again in 50ms
+ */
+ lws_sul_schedule(lds->ctx, 0, &lds->sul,
+ sul_cb, 50 * LWS_US_PER_MS);
+}
+
+static void
+sul_autodim_cb(lws_sorted_usec_list_t *sul)
+{
+ lws_display_state_t *lds = lws_container_of(sul, lws_display_state_t,
+ sul_autodim);
+
+ /* we fire both to dim and to blank... if already in dim state, blank */
+
+ if (lds->state == LWSDISPS_AUTODIMMED) {
+ lws_display_state_off(lds);
+ return;
+ }
+
+ lds->state = LWSDISPS_AUTODIMMED;
+ lws_display_state_set_brightness(lds, lds->bl_dim, lds->bl_step);
+
+ if (lds->off_ms >= 0)
+ lws_sul_schedule(lds->ctx, 0, &lds->sul_autodim, sul_autodim_cb,
+ lds->off_ms * LWS_US_PER_MS);
+}
+
+void
+lws_display_state_init(lws_display_state_t *ds, struct lws_context *ctx,
+ int dim_ms, int off_ms, lws_display_brightness active,
+ lws_display_brightness dim, const lws_display_t *disp)
+{
+ memset(ds, 0, sizeof(*ds));
+ ds->disp = disp;
+ ds->ctx = ctx;
+ ds->autodim_ms = dim_ms;
+ ds->off_ms = off_ms;
+ ds->bl_active = active;
+ ds->bl_dim = dim;
+}
+
+void
+lws_display_state_set_brightness(lws_display_state_t *lds,
+ lws_display_brightness target,
+ lws_display_brightness step)
+{
+ lds->bl_target = target;
+ lds->bl_step = step;
+
+ lws_sul_schedule(lds->ctx, 0, &lds->sul, sul_cb, 1);
+}
+
+void
+lws_display_state_active(lws_display_state_t *lds)
+{
+ if (lds->state == LWSDISPS_OFF)
+ lds->disp->power(lds->disp, 1);
+
+ if (lds->bl_current != lds->bl_active)
+ lws_display_state_set_brightness(lds, lds->bl_active, 2);
+
+ /* reset the autodim timer */
+ if (lds->autodim_ms >= 0)
+ lws_sul_schedule(lds->ctx, 0, &lds->sul_autodim, sul_autodim_cb,
+ lds->autodim_ms * LWS_US_PER_MS);
+
+ lds->state = LWSDISPS_ACTIVE;
+}
+
+void
+lws_display_state_off(lws_display_state_t *lds)
+{
+ lds->disp->power(lds->disp, 0);
+ lws_sul_cancel(&lds->sul);
+ lws_sul_cancel(&lds->sul_autodim);
+ lds->bl_current = 0;
+ lds->state = LWSDISPS_OFF;
+}
diff --git a/lib/drivers/display/ssd1306-i2c.c b/lib/drivers/display/ssd1306-i2c.c
new file mode 100644
index 00000000..a050271f
--- /dev/null
+++ b/lib/drivers/display/ssd1306-i2c.c
@@ -0,0 +1,151 @@
+/*
+ * lws abstract display implementation for ssd1306 on i2c
+ *
+ * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * 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 <libwebsockets.h>
+#include <drivers/devices/display/ssd1306.h>
+
+
+static uint8_t ssd1306_128x64_init[] = {
+ SSD1306_DISPLAYOFF,
+ SSD1306_SETDISPLAYCLOCKDIV, 0xf0,
+ SSD1306_SETMULTIPLEX, 64 - 1,
+ SSD1306_SETDISPLAYOFFSET, 0,
+ SSD1306_CHARGEPUMP, 0x14,
+ SSD1306_MEMORYMODE, 0,
+ SSD1306_SEGREMAP | (0 << 0),
+ SSD1306_COMSCANDEC,
+ SSD1306_SETCOMPINS, (1 << 4) | 0x02,
+ SSD1306_SETCONTRAST, 0, /* start at lowest */
+ SSD1306_SETPRECHARGE, (0xf << 4) | (1 << 0),
+ SSD1306_SETVCOMDESELECT, (4 << 4),
+ SSD1306_DEACTIVATE_SCROLL,
+ SSD1306_DISPLAYALLON_RESUME,
+ SSD1306_NORMALDISPLAY,
+ SSD1306_DISPLAYON
+};
+
+int
+lws_display_ssd1306_i2c_init(const struct lws_display *disp)
+{
+ const lws_display_ssd1306_t *si = (const lws_display_ssd1306_t *)disp;
+
+ si->i2c->init(si->i2c);
+
+ if (si->gpio) {
+ si->gpio->mode(si->reset_gpio, LWSGGPIO_FL_WRITE |
+ LWSGGPIO_FL_PULLUP);
+ si->gpio->set(si->reset_gpio, 0);
+
+#if defined(LWS_PLAT_FREERTOS)
+ vTaskDelay(10);
+#else
+ usleep(10000);
+#endif
+ si->gpio->set(si->reset_gpio, 1);
+#if defined(LWS_PLAT_FREERTOS)
+ vTaskDelay(1);
+#else
+ usleep(1000);
+#endif
+ }
+
+ if (lws_i2c_command_list(si->i2c, si->i2c7_address,
+ ssd1306_128x64_init,
+ LWS_ARRAY_SIZE(ssd1306_128x64_init))) {
+ lwsl_err("%s: fail\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+lws_display_ssd1306_i2c_brightness(const struct lws_display *disp, uint8_t b)
+{
+ const lws_display_ssd1306_t *si = (const lws_display_ssd1306_t *)disp;
+ uint8_t ba[2];
+
+ ba[0] = SSD1306_SETCONTRAST;
+ ba[1] = b;
+
+ return lws_i2c_command_list(si->i2c, si->i2c7_address,
+ ba, LWS_ARRAY_SIZE(ba));
+}
+
+int
+lws_display_ssd1306_i2c_blit(const struct lws_display *disp, const uint8_t *src,
+ lws_display_scalar x, lws_display_scalar y,
+ lws_display_scalar w, lws_display_scalar h)
+{
+ const lws_display_ssd1306_t *si = (const lws_display_ssd1306_t *)disp;
+ uint8_t ba[6];
+ int n, m;
+
+ /*
+ * The display is arranged in 128x8 bands, with one byte containing
+ * the 8 vertical pixels of the band.
+ */
+
+ if (h < 8)
+ h = 8;
+
+ ba[0] = SSD1306_COLUMNADDR;
+ ba[1] = x;
+ ba[2] = x + w - 1;
+ ba[3] = SSD1306_PAGEADDR;
+ ba[4] = y / 8;
+ ba[5] = ba[4] + (h / 8) - 1;
+
+ if (lws_i2c_command_list(si->i2c, si->i2c7_address,
+ ba, LWS_ARRAY_SIZE(ba))) {
+ lwsl_err("%s: fail\n", __func__);
+ return 1;
+ }
+
+ for (n = 0; n < (w * h) / 8;) {
+ lws_bb_i2c_start(si->i2c);
+ lws_bb_i2c_write(si->i2c, si->i2c7_address << 1);
+ lws_bb_i2c_write(si->i2c, SSD1306_SETSTARTLINE | y);
+
+ for (m = 0; m < w; m++)
+ lws_bb_i2c_write(si->i2c, src[n++]);
+
+ lws_bb_i2c_stop(si->i2c);
+ y += 8;
+ }
+
+ return 0;
+}
+
+int
+lws_display_ssd1306_i2c_power(const struct lws_display *disp, int state)
+{
+ const lws_display_ssd1306_t *si = (const lws_display_ssd1306_t *)disp;
+
+ if (!state)
+ return lws_i2c_command(si->i2c, si->i2c7_address,
+ SSD1306_DISPLAYOFF | !!state);
+
+ return lws_display_ssd1306_i2c_init(disp);
+}
diff --git a/lib/drivers/i2c/bitbang/lws-bb-i2c.c b/lib/drivers/i2c/bitbang/lws-bb-i2c.c
new file mode 100644
index 00000000..99e92a55
--- /dev/null
+++ b/lib/drivers/i2c/bitbang/lws-bb-i2c.c
@@ -0,0 +1,135 @@
+/*
+ * I2C bitbang implementation using generic gpio
+ *
+ * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * 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.
+ *
+ * This is like an abstract class for gpio, a real implementation provides
+ * functions for the ops that use the underlying OS gpio arrangements.
+ */
+#include <libwebsockets.h>
+
+int
+lws_bb_i2c_init(const lws_i2c_ops_t *octx)
+{
+ lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx;
+
+ ctx->gpio->mode(ctx->scl, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_READ | LWSGGPIO_FL_PULLUP);
+ ctx->gpio->mode(ctx->sda, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_READ | LWSGGPIO_FL_PULLUP);
+
+ return 0;
+}
+
+int
+lws_bb_i2c_start(const lws_i2c_ops_t *octx)
+{
+ lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx;
+
+ ctx->gpio->set(ctx->sda, 1);
+ ctx->gpio->set(ctx->scl, 1);
+ ctx->delay();
+
+ if (!ctx->gpio->read(ctx->sda))
+ return 1;
+
+ ctx->gpio->set(ctx->sda, 0);
+ ctx->delay();
+ ctx->gpio->set(ctx->scl, 0);
+
+ return 0;
+}
+
+void
+lws_bb_i2c_stop(const lws_i2c_ops_t *octx)
+{
+ lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx;
+
+ ctx->gpio->set(ctx->sda, 0);
+ ctx->gpio->set(ctx->scl, 1);
+ ctx->delay();
+
+ while (!ctx->gpio->read(ctx->scl))
+ ;
+
+ ctx->gpio->set(ctx->sda, 1);
+ ctx->delay();
+}
+
+int
+lws_bb_i2c_write(const lws_i2c_ops_t *octx, uint8_t data)
+{
+ lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx;
+ int n;
+
+ for (n = 0; n < 8; n++) {
+ ctx->gpio->set(ctx->sda, !!(data & (1 << 7)));
+ ctx->delay();
+ ctx->gpio->set(ctx->scl, 1);
+ ctx->delay();
+ data <<= 1;
+ ctx->gpio->set(ctx->scl, 0);
+ }
+
+ ctx->gpio->set(ctx->sda, 1);
+ ctx->delay();
+ ctx->gpio->set(ctx->scl, 1);
+ ctx->delay();
+ n = ctx->gpio->read(ctx->sda);
+ ctx->gpio->set(ctx->scl, 0);
+ ctx->delay();
+
+ return !!n; /* 0 = ACKED = OK */
+}
+
+int
+lws_bb_i2c_read(const lws_i2c_ops_t *octx)
+{
+ lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx;
+ int n, r = 0;
+
+ ctx->gpio->set(ctx->sda, 1);
+
+ for (n = 7; n <= 0; n--) {
+ ctx->gpio->set(ctx->scl, 0);
+ ctx->delay();
+ ctx->gpio->set(ctx->scl, 1);
+ ctx->delay();
+ if (ctx->gpio->read(ctx->sda))
+ r |= 1 << n;
+ }
+ ctx->gpio->set(ctx->scl, 0);
+
+ return r;
+}
+
+void
+lws_bb_i2c_set_ack(const lws_i2c_ops_t *octx, int ack)
+{
+ lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx;
+
+ ctx->gpio->set(ctx->scl, 0);
+ ctx->gpio->set(ctx->sda, !!ack);
+ ctx->delay();
+ ctx->gpio->set(ctx->scl, 1);
+ ctx->delay();
+ ctx->gpio->set(ctx->scl, 0);
+ ctx->delay();
+ ctx->gpio->set(ctx->sda, 1);
+}
diff --git a/lib/drivers/i2c/lws-i2c.c b/lib/drivers/i2c/lws-i2c.c
new file mode 100644
index 00000000..fa00008f
--- /dev/null
+++ b/lib/drivers/i2c/lws-i2c.c
@@ -0,0 +1,59 @@
+/*
+ * Generic I2C
+ *
+ * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * 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.
+ *
+ * These are generic helpers made up of calls to the i2c driver ops, so they
+ * just need implementing once like this and are usable for any i2c underlying
+ * implementation via the ops.
+ */
+
+#include <libwebsockets.h>
+
+int
+lws_i2c_command(const lws_i2c_ops_t *ctx, uint8_t ads7, uint8_t c)
+{
+ if (ctx->start(ctx))
+ return 1;
+
+ if (ctx->write(ctx, ads7 << 1)) {
+ ctx->stop(ctx);
+
+ return 1;
+ }
+
+ ctx->write(ctx, 0);
+ ctx->write(ctx, c);
+ ctx->stop(ctx);
+
+ return 0;
+}
+
+int
+lws_i2c_command_list(const lws_i2c_ops_t *ctx, uint8_t ads7, const uint8_t *buf,
+ size_t len)
+{
+ while (len--)
+ if (lws_i2c_command(ctx, ads7, *buf++))
+ return 1;
+
+ return 0;
+}