blob: 5a88d15303fde96a2d1bc8b18aac9a2d50de3bfb [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_key_pool.h"
#include <stdlib.h>
/* Key pool denouncing settle time */
#define KEY_POOL_DEBOUNCING_GUARD_MS 10
/* Auxiliary data to link key pool with pin isr */
struct key_pool_aux_data
{
struct key_pool_data * key_pool;
const struct device * port;
struct gpio_callback callback;
};
/* Auxiliary function to get ports number in pool */
static size_t key_pool_port_number(const struct key_pool_data * key_pool)
{
size_t port_num = 0;
for (size_t i = 0; i < key_pool->inp_len; i++)
{
bool port_already = false;
for (size_t j = 0; j < i; j++)
{
if (key_pool->inp[i].port == key_pool->inp[j].port)
{
port_already = true;
break;
}
}
if (!port_already)
{
port_num++;
}
}
return port_num;
}
/* Poll key pool and rise event on key change */
static void key_pool_poll(struct key_pool_data * key_pool, bool init)
{
for (size_t i = 0; i < key_pool->inp_len; i++)
{
bool pin = gpio_pin_get_dt(&key_pool->inp[i]);
if (pin != (bool) (key_pool->buttons[i / 8] & BIT(i % 8)))
{
WRITE_BIT(key_pool->buttons[i / 8], i % 8, pin);
if (!init && key_pool->on_button_change)
{
key_pool->on_button_change(i, pin, key_pool->context);
}
}
}
}
/* Key pool scan worker */
static void key_pool_event_work(struct k_work * item)
{
struct key_pool_data * key_pool = CONTAINER_OF(item, struct key_pool_data, work);
key_pool_poll(key_pool, false);
}
/* Key pool pin isr */
static void key_pool_on_pin_isr(const struct device * dev, struct gpio_callback * cb, uint32_t pins)
{
struct key_pool_aux_data * key_pool_aux = CONTAINER_OF(cb, struct key_pool_aux_data, callback);
(void) k_work_reschedule(&key_pool_aux->key_pool->work, K_MSEC(KEY_POOL_DEBOUNCING_GUARD_MS));
}
/* Public APIs */
bool key_pool_init(struct key_pool_data * key_pool)
{
bool result = true;
do
{
if (!key_pool->inp_len)
{
result = false;
break;
}
/* check if all GPIOs are ready */
for (size_t i = 0; i < key_pool->inp_len; i++)
{
if (!gpio_is_ready_dt(&key_pool->inp[i]))
{
result = false;
break;
}
}
if (!result)
{
break;
}
/* init all GPIOs are ready */
for (size_t i = 0; i < key_pool->inp_len; i++)
{
if (gpio_pin_configure_dt(&key_pool->inp[i], GPIO_INPUT))
{
result = false;
break;
}
if (gpio_pin_interrupt_configure_dt(&key_pool->inp[i], GPIO_INT_EDGE_BOTH))
{
result = false;
break;
}
}
if (!result)
{
break;
}
/* add callbacks to all ports */
struct key_pool_aux_data * key_pool_aux =
(struct key_pool_aux_data *) malloc(sizeof(struct key_pool_aux_data) * key_pool_port_number(key_pool));
if (!key_pool_aux)
{
result = false;
break;
}
key_pool->aux = key_pool_aux;
size_t key_pool_aux_inited_cnt = 0;
for (size_t i = 0; i < key_pool->inp_len; i++)
{
bool port_found = false;
for (size_t j = 0; j < key_pool_aux_inited_cnt; j++)
{
if (key_pool->inp[i].port == key_pool_aux[j].port)
{
port_found = true;
key_pool_aux[j].callback.pin_mask |= BIT(key_pool->inp[i].pin);
}
}
if (!port_found)
{
key_pool_aux[key_pool_aux_inited_cnt].key_pool = key_pool;
key_pool_aux[key_pool_aux_inited_cnt].port = key_pool->inp[i].port;
gpio_init_callback(&key_pool_aux[key_pool_aux_inited_cnt].callback, key_pool_on_pin_isr, BIT(key_pool->inp[i].pin));
key_pool_aux_inited_cnt++;
}
}
for (size_t i = 0; i < key_pool_aux_inited_cnt; i++)
{
if (gpio_add_callback(key_pool_aux[i].port, &key_pool_aux[i].callback))
{
result = false;
break;
}
}
if (!result)
{
break;
}
/* set all keys to current state */
key_pool_poll(key_pool, true);
/* init work */
k_work_init_delayable(&key_pool->work, key_pool_event_work);
/* all done */
} while (0);
return result;
}
void key_pool_set_callback(struct key_pool_data * key_pool, key_pool_on_button_change_t on_button_change, void * context)
{
key_pool->on_button_change = on_button_change;
key_pool->context = context;
}