blob: f8ced2d413fd63e11a633d3786a8ffc730d50964 [file] [log] [blame]
/*
*
* Copyright (c) 2024 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "zephyr_led_pool.h"
#include <stdarg.h>
#include <stdlib.h>
/* Auxiliary data to support blink */
struct led_pool_aux_data
{
enum led_state state;
k_ticks_t t_event;
k_timeout_t t_on;
k_timeout_t t_off;
bool current_state;
};
/* Get timeout to next event in auxiliary data */
static k_timeout_t led_pool_aux_timeout(const struct led_pool_data * led_pool)
{
k_timeout_t timeout = { .ticks = K_TICKS_FOREVER };
const struct led_pool_aux_data * led_pool_aux = (const struct led_pool_aux_data *) led_pool->aux;
size_t led_pool_aux_len = led_pool->out_len;
for (size_t i = 0; i < led_pool_aux_len; i++)
{
if (led_pool_aux[i].state == LED_BLINK)
{
k_ticks_t t_next = led_pool_aux[i].t_event +
(led_pool_aux[i].current_state ? led_pool_aux[i].t_on.ticks : led_pool_aux[i].t_off.ticks);
k_ticks_t t_now = sys_clock_tick_get();
k_timeout_t this = { .ticks = t_next > t_now ? t_next - t_now : 0 };
if (timeout.ticks == K_TICKS_FOREVER || this.ticks < timeout.ticks)
{
timeout.ticks = this.ticks;
}
}
}
return timeout;
}
/* Process events in auxiliary data */
static void led_pool_aux_update(const struct led_pool_data * led_pool)
{
struct led_pool_aux_data * led_pool_aux = (struct led_pool_aux_data *) led_pool->aux;
size_t led_pool_aux_len = led_pool->out_len;
for (size_t i = 0; i < led_pool_aux_len; i++)
{
if (led_pool_aux[i].state == LED_BLINK)
{
k_ticks_t t_next = led_pool_aux[i].t_event +
(led_pool_aux[i].current_state ? led_pool_aux[i].t_on.ticks : led_pool_aux[i].t_off.ticks);
k_ticks_t t_now = sys_clock_tick_get();
if (t_next <= t_now)
{
led_pool_aux[i].current_state = !led_pool_aux[i].current_state;
led_pool_aux[i].t_event = t_now;
(void) gpio_pin_set_dt(&led_pool->out[i], led_pool_aux[i].current_state);
}
}
}
}
/* Led pool worker */
static void led_pool_event_work(struct k_work * item)
{
struct led_pool_data * led_pool = CONTAINER_OF(item, struct led_pool_data, work);
led_pool_aux_update(led_pool);
(void) k_work_reschedule(&led_pool->work, led_pool_aux_timeout(led_pool));
}
/* Public APIs */
bool led_pool_init(struct led_pool_data * led_pool)
{
bool result = true;
do
{
if (!led_pool->out_len)
{
result = false;
break;
}
/* check if all GPIOs are ready */
for (size_t i = 0; i < led_pool->out_len; i++)
{
if (!gpio_is_ready_dt(&led_pool->out[i]))
{
result = false;
break;
}
}
if (!result)
{
break;
}
/* init all GPIOs are ready */
for (size_t i = 0; i < led_pool->out_len; i++)
{
if (gpio_pin_configure_dt(&led_pool->out[i], GPIO_OUTPUT))
{
result = false;
break;
}
if (gpio_pin_set_dt(&led_pool->out[i], 0))
{
result = false;
break;
}
}
if (!result)
{
break;
}
/* set auxiliary blink structure */
struct led_pool_aux_data * led_pool_aux =
(struct led_pool_aux_data *) malloc(sizeof(struct led_pool_aux_data) * led_pool->out_len);
if (!led_pool_aux)
{
result = false;
break;
}
for (size_t i = 0; i < led_pool->out_len; i++)
{
led_pool_aux[i].state = LED_OFF;
}
led_pool->aux = led_pool_aux;
/* init work */
k_work_init_delayable(&led_pool->work, led_pool_event_work);
if (k_work_reschedule(&led_pool->work, led_pool_aux_timeout(led_pool)) < 0)
{
result = false;
break;
}
/* all done */
} while (0);
return result;
}
bool led_pool_set(struct led_pool_data * led_pool, size_t led, enum led_state state, ...)
{
bool result = false;
if (led < led_pool->out_len)
{
if (state == LED_ON || state == LED_OFF)
{
if (!gpio_pin_set_dt(&led_pool->out[led], state))
{
struct led_pool_aux_data * led_pool_aux = led_pool->aux;
led_pool_aux[led].state = state;
if (k_work_reschedule(&led_pool->work, led_pool_aux_timeout(led_pool)) >= 0)
{
result = true;
}
}
}
else if (state == LED_BLINK)
{
if (!gpio_pin_set_dt(&led_pool->out[led], 1))
{
struct led_pool_aux_data * led_pool_aux = led_pool->aux;
va_list argptr;
va_start(argptr, state);
led_pool_aux[led].state = state;
led_pool_aux[led].t_event = sys_clock_tick_get();
led_pool_aux[led].t_on = va_arg(argptr, k_timeout_t);
led_pool_aux[led].t_off = va_arg(argptr, k_timeout_t);
led_pool_aux[led].current_state = true;
va_end(argptr);
if (k_work_reschedule(&led_pool->work, led_pool_aux_timeout(led_pool)) >= 0)
{
result = true;
}
}
}
}
return result;
}