blob: dd9efd20a366c003c36e3ccb96ca5d00ae15810c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <string.h>
#include "os/mynewt.h"
#include <sys/queue.h>
#include <battery/battery.h>
#include <battery/battery_prop.h>
#include <battery/battery_drv.h>
#define BATTERY_MAX_COUNT 1
#define GET_BIT(arr, bit) ((arr)[(bit) / 32] & (1 << ((bit) % 32)))
/*
* Structure holds information about listener and what is it listening for.
*/
struct listener_data
{
/* The properties to monitor for change */
uint32_t ld_prop_change_mask[BATTERY_PROPERTY_MASK_SIZE];
/* The properties to monitor for periodic read */
uint32_t ld_prop_read_mask[BATTERY_PROPERTY_MASK_SIZE];
/* Pointer to listener provided by the application. */
struct battery_prop_listener *ld_listener;
};
struct battery_manager
{
/* The lock for battery object */
struct os_mutex bm_lock;
struct battery *bm_batteries[BATTERY_MAX_COUNT];
/** Event queue */
struct os_eventq *bm_eventq;
/** Manages poll rates of the batteries */
struct os_callout bm_poll_callout;
} battery_manager;
struct os_eventq *
battery_mgr_evq_get(void)
{
return (battery_manager.bm_eventq);
}
static void
battery_mgr_evq_set(struct os_eventq *evq)
{
assert(evq != NULL);
battery_manager.bm_eventq = evq;
}
static void battery_mgr_poll_battery(struct battery *battery);
static void
battery_poll_event_cb(struct os_event *ev)
{
int i;
int pflag;
os_time_t next_poll = 0;
os_time_t now = os_time_get();
struct battery *bat;
os_stime_t ticks;
pflag = 0;
for (i = 0; i < BATTERY_MAX_COUNT; ++i) {
bat = battery_manager.bm_batteries[i];
if (bat) {
if (OS_TIME_TICK_GEQ(now, bat->b_next_run)) {
bat->b_last_read_time = now;
battery_mgr_poll_battery(battery_manager.bm_batteries[i]);
bat->b_next_run = now + os_time_ms_to_ticks32(bat->b_poll_rate);
}
if ((pflag == 0) || OS_TIME_TICK_LT(bat->b_next_run, next_poll)) {
pflag = 1;
next_poll = bat->b_next_run;
}
}
}
if (pflag) {
ticks = (os_stime_t)(next_poll - os_time_get());
if (ticks < 0) {
ticks = 1;
}
os_callout_reset(&battery_manager.bm_poll_callout, (os_time_t)ticks);
}
}
void
battery_mgr_process_event(struct os_event *event)
{
os_eventq_put(battery_manager.bm_eventq, event);
}
static void
battery_mgr_init(void)
{
#ifdef MYNEWT_VAL_BATTERY_MGR_EVQ
battery_mgr_evq_set(MYNEWT_VAL(BATTERY_MGR_EVQ));
#else
battery_mgr_evq_set(os_eventq_dflt_get());
#endif
/**
* Initialize battery manager polling callout.
*/
os_callout_init(&battery_manager.bm_poll_callout,
battery_mgr_evq_get(), battery_poll_event_cb,
NULL);
os_mutex_init(&battery_manager.bm_lock);
}
/* =================================================================
* ====================== PKG ======================================
* =================================================================
*/
void
battery_pkg_init(void)
{
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
battery_mgr_init();
#if MYNEWT_VAL(BATTERY_SHELL)
battery_shell_register();
#endif
}
int
battery_mgr_register(struct battery *battery)
{
int i;
os_mutex_pend(&battery_manager.bm_lock, OS_WAIT_FOREVER);
for (i = 0; i < BATTERY_MAX_COUNT; ++i) {
if (battery_manager.bm_batteries[i] == NULL) {
battery_manager.bm_batteries[i] = battery;
}
}
os_mutex_release(&battery_manager.bm_lock);
return 0;
}
int
battery_mgr_get_battery_count(void)
{
int i;
for (i = 0; i < BATTERY_MAX_COUNT; ++i) {
if (battery_manager.bm_batteries[i] == NULL) {
break;
}
}
return i;
}
struct os_dev *
battery_get_battery(int bat_num)
{
assert(bat_num < BATTERY_MAX_COUNT);
assert(battery_manager.bm_batteries[bat_num]);
return &battery_manager.bm_batteries[bat_num]->b_dev;
}
static int
battery_get_num(struct battery *battery)
{
int i;
/* For one battery, there is no need to specify battery at all */
if (battery == NULL) {
return 0;
}
for (i = 0; i < BATTERY_MAX_COUNT; ++i) {
if (battery_manager.bm_batteries[i] == battery) {
return i;
}
}
assert(0);
return 0;
}
struct battery_driver *
battery_get_driver(struct os_dev *battery,
const char *dev_name)
{
int i;
assert(battery);
struct battery *bat = (struct battery *)battery;
for (i = 0; i < BATTERY_DRIVERS_MAX && bat->b_drivers[i]; ++i) {
if (strcmp(bat->b_drivers[i]->dev.od_name, dev_name) == 0) {
return bat->b_drivers[i];
}
}
/* No device found */
assert(0);
return NULL;
}
static void
set_bit(uint32_t *mask, int bit)
{
mask[bit / (sizeof(*mask) * 8)] |=
1 << (bit & ((sizeof(*mask) * 8) - 1));
}
static void
clear_bit(uint32_t *mask, int bit)
{
mask[bit / (sizeof(*mask) * 8)] &=
~(1 << (bit & ((sizeof(*mask) * 8) - 1)));
}
static struct battery_property *
find_driver_property(struct battery *bat, struct battery_driver *driver,
battery_property_type_t type, battery_property_flags_t flags)
{
struct battery_property *prop = bat->b_properties + driver->bd_first_property;
int i;
assert(driver);
for (i = 0; driver->bd_property_count; ++i, ++prop) {
if (prop->bp_type == type && prop->bp_flags == flags)
return prop;
}
return NULL;
}
static struct battery_property *
find_hardware_property(struct battery *battery, struct battery_driver *driver,
battery_property_type_t type, battery_property_flags_t flags)
{
struct battery_property *res = NULL;
int i;
/* Search driver properties first */
if (driver) {
res = find_driver_property(battery, driver, type, flags);
} else {
for (i = 0; res == NULL && i < BATTERY_DRIVERS_MAX; ++i) {
res = find_driver_property(battery, battery->b_drivers[i],
type, flags);
}
}
return res;
}
struct battery_property *
battery_find_property(struct os_dev *battery,
battery_property_type_t type, battery_property_flags_t flags,
const char *dev_name)
{
struct battery_property *res = NULL;
struct battery_property *prop;
struct battery *bat = (struct battery *)battery;
int i;
struct battery_driver *driver = NULL;
if (dev_name) {
driver = battery_get_driver(battery, dev_name);
assert(driver);
}
/* Search hardware properties first */
res = find_hardware_property(bat, driver, type, flags);
if (!res) {
/* Search battery manager created properties */
for (i = 0; i < bat->b_all_property_count; ++i) {
prop = &bat->b_properties[i];
if (prop->bp_type == type &&
prop->bp_flags == flags &&
(driver == NULL ||
bat->b_drivers[prop->bp_drv_num] == driver)) {
res = prop;
break;
}
}
}
if (!res) {
/* Create software threshold */
if ((flags & BATTERY_PROPERTY_FLAGS_CREATE) != 0 &&
(flags & BATTERY_PROPERTY_FLAGS_ALARM_THREASH) != 0) {
/* Find base property that threshold is for */
prop = find_hardware_property(bat, driver, type,
BATTERY_PROPERTY_FLAGS_NONE);
/* Base property found, create derived one */
if (prop) {
struct battery_property *p =
calloc(1, sizeof(struct battery_property));
if (prop->bp_prop_num == 0) {
prop->bp_prop_num = ++bat->b_all_property_count;
assert(bat->b_all_property_count <= BATTERY_MAX_PROPERTY_COUNT);
}
/* Base property is marked as base for others
*/
prop->bp_base = 1;
p->bp_type = type;
p->bp_flags = flags;
p->bp_bat_num = prop->bp_bat_num;
p->bp_drv_num = prop->bp_drv_num;
p->bp_prop_num = ++bat->b_all_property_count;
assert(bat->b_all_property_count <= BATTERY_MAX_PROPERTY_COUNT);
res = p;
}
}
}
return res;
}
int
battery_get_property_count(struct os_dev *battery,
struct battery_driver *driver)
{
struct battery *bat = (struct battery *)battery;
int i;
if (driver == NULL) {
return bat->b_all_property_count;
} else {
for (i = 0; i < BATTERY_DRIVERS_MAX && bat->b_drivers[i]; ++i) {
if (bat->b_drivers[i] == driver) {
return bat->b_drivers[i]->bd_property_count;
}
}
}
return 0;
}
struct battery_property *
battery_enum_property(struct os_dev *battery, struct battery_driver *driver,
uint8_t prop_num)
{
struct battery *bat = (struct battery *)battery;
struct battery_property *prop = NULL;
int i;
if (driver == NULL) {
if (prop_num < bat->b_all_property_count) {
prop = &bat->b_properties[prop_num];
}
} else {
for (i = 0; i < BATTERY_DRIVERS_MAX && bat->b_drivers[i]; ++i) {
if (bat->b_drivers[i] == driver) {
if (prop_num < driver->bd_property_count) {
prop = &bat->b_properties[prop_num + driver->bd_first_property];
}
}
}
}
return prop;
}
char *
battery_prop_get_name(const struct battery_property *prop, char *buf,
size_t buf_size)
{
struct battery *bat = battery_manager.bm_batteries[prop->bp_bat_num];
struct battery_driver *driver = bat->b_drivers[prop->bp_drv_num];
const struct battery_driver_property *driver_prop =
&driver->bd_driver_properties[prop->bp_prop_num - driver->bd_first_property];
strncpy(buf, driver_prop->bdp_name, buf_size);
return buf;
}
struct battery_property *
battery_find_property_by_name(struct os_dev *battery, const char *name)
{
struct battery *bat = (struct battery *)battery;
char buf[20];
int i;
for (i = 0; i < bat->b_all_property_count; ++i) {
battery_prop_get_name(&bat->b_properties[i], buf, 20);
if (strcmp(buf, name) == 0) {
return &bat->b_properties[i];
}
}
return NULL;
}
static void
battery_mgr_poll_battery_driver(struct battery *bat,
struct battery_driver *drv, uint32_t changed[], uint32_t queried[])
{
int i;
battery_property_value_t old_val;
struct battery_property *prop = &bat->b_properties[drv->bd_first_property];
/* Read requested properties */
for (i = 0; i < drv->bd_property_count; ++i, ++prop) {
if (!GET_BIT(bat->b_polled_properties, i)) {
continue;
}
if (!driver_property(prop)) {
continue;
}
old_val = prop->bp_value;
if (drv->bd_funcs->bdf_property_get(drv, prop, 100)) {
prop->bp_valid = 0;
} else {
prop->bp_valid = 1;
if (memcmp(&old_val, &prop->bp_value, sizeof(old_val))) {
set_bit(changed, prop->bp_prop_num);
}
set_bit(queried, prop->bp_prop_num);
}
}
}
static void
battery_mgr_poll_battery(struct battery *battery)
{
uint32_t changed[BATTERY_PROPERTY_MASK_SIZE] = {0};
uint32_t queried[BATTERY_PROPERTY_MASK_SIZE] = {0};
uint32_t masked;
int first_one;
struct listener_data *ld;
struct battery_driver *driver;
int i;
int j;
/* Poll battery drivers */
for(i = 0; i < BATTERY_DRIVERS_MAX; ++i) {
driver = battery->b_drivers[i];
if (driver) {
battery_mgr_poll_battery_driver(battery, driver, changed, queried);
}
}
/* Notify listeners about property changes */
for (i = 0; i < battery->b_listener_count; ++i) {
ld = &battery->b_listeners[i];
for (j = 0; j < BATTERY_PROPERTY_MASK_SIZE; ++j) {
masked = changed[j] & ld->ld_prop_change_mask[j];
while (masked) {
/* Find first set bit */
first_one = __builtin_ffs(masked) - 1;
/* Clear it for next loop */
masked ^= (1 << first_one);
driver = battery->b_drivers[i];
/* Notify listener */
ld->ld_listener->bpl_prop_changed(ld->ld_listener,
battery->b_properties +
first_one + j * 32);
}
}
}
/* Notify listeners about periodic reads */
for (i = 0; i < battery->b_listener_count; ++i) {
ld = &battery->b_listeners[i];
for (j = 0; j < BATTERY_PROPERTY_MASK_SIZE; ++j) {
masked = queried[j] & ld->ld_prop_read_mask[j];
while (masked) {
/* Find first set bit */
first_one = __builtin_ffs(masked) - 1;
/* Clear it for next loop */
masked ^= (1 << first_one);
driver = battery->b_drivers[i];
/* Notify listener */
ld->ld_listener->bpl_prop_read(ld->ld_listener,
battery->b_properties +
first_one + j * 32);
}
}
}
}
int
battery_set_poll_rate_ms(struct os_dev *battery, uint32_t poll_rate)
{
struct battery *bat = (struct battery *)battery;
if ((poll_rate == 0) || (bat == NULL)) {
return -1;
}
bat->b_poll_rate = poll_rate;
bat->b_next_run = os_time_get();
os_callout_reset(&battery_manager.bm_poll_callout, 0);
return 0;
}
int
battery_add_driver(struct os_dev *battery, struct battery_driver *driver)
{
struct battery *bat = (struct battery *)battery;
const struct battery_driver_property *drv_prop;
struct battery_property *prop;
int bat_num;
int drv_num;
int i;
int j;
assert(battery);
assert(driver);
bat_num = battery_get_num(bat);
/* Find first slot */
for (i = 0; i < BATTERY_DRIVERS_MAX; ++i) {
/* Just make sure that driver is not added twice */
assert(bat->b_drivers[i] != driver);
if (bat->b_drivers[i] == NULL) {
break;
}
}
assert(i < BATTERY_DRIVERS_MAX);
drv_num = i;
bat->b_drivers[i] = driver;
drv_prop = driver->bd_driver_properties;
for (i = 0; drv_prop->bdp_type; ++i) {
++drv_prop;
}
driver->bd_property_count = i;
bat->b_properties = realloc(bat->b_properties,
((i + bat->b_all_property_count) *
sizeof(struct battery_property)));
j = bat->b_all_property_count;
driver->bd_first_property = (uint8_t)j;
/* Initialize driver properties with battery manager data */
for (i = 0; i < driver->bd_property_count; ++i, ++j) {
prop = bat->b_properties + j;
prop->bp_type = driver->bd_driver_properties[i].bdp_type;
prop->bp_flags = driver->bd_driver_properties[i].bdp_flags;
prop->bp_valid = 0;
prop->bp_value.bpv_i32 = 0;
prop->bp_drv_prop_num = (uint8_t)i;
prop->bp_drv_num = drv_num;
prop->bp_bat_num = bat_num;
prop->bp_prop_num = j;
}
bat->b_all_property_count = j;
return 0;
}
static int
get_listener_index(struct battery *battery,
struct battery_prop_listener *listener)
{
int i;
assert(listener);
assert(battery);
/* Find existing listener */
for (i = 0; i < battery->b_listener_count; ++i) {
if (battery->b_listeners[i].ld_listener == listener) {
break;
}
}
/* If listener was not yet added, create space for it and fill
* masks with zeros so they will be filled when listener is actually
* added to properties.
*/
if (i >= battery->b_listener_count) {
i = battery->b_listener_count++;
battery->b_listeners = realloc(battery->b_listeners,
sizeof(struct listener_data) * battery->b_listener_count);
if (battery->b_listeners == NULL) {
return -1;
}
battery->b_listeners[i].ld_listener = listener;
memset(battery->b_listeners[i].ld_prop_change_mask, 0,
sizeof(battery->b_listeners[i].ld_prop_change_mask));
memset(battery->b_listeners[i].ld_prop_read_mask, 0,
sizeof(battery->b_listeners[i].ld_prop_read_mask));
}
return i;
}
static int
battery_update_polled_properties(struct battery *battery)
{
struct listener_data *ld;
int i;
int j;
for (i = 0; i < BATTERY_PROPERTY_MASK_SIZE; ++i) {
battery->b_polled_properties[i] = 0;
for (j = 0; j < battery->b_listener_count; ++j) {
ld = &battery->b_listeners[j];
battery->b_polled_properties[i] |= ld->ld_prop_read_mask[i];
battery->b_polled_properties[i] |= ld->ld_prop_change_mask[i];
}
}
return 0;
}
int
battery_prop_change_subscribe(struct battery_prop_listener *listener,
struct battery_property *prop)
{
struct battery *battery;
int listener_index;
assert(listener);
assert(prop);
battery = (struct battery *)battery_get_battery(prop->bp_bat_num);
listener_index = get_listener_index(battery, listener);
set_bit(battery->b_listeners[listener_index].ld_prop_change_mask,
prop->bp_prop_num);
battery_update_polled_properties(battery);
return 0;
}
int
battery_prop_change_unsubscribe(struct battery_prop_listener *listener,
struct battery_property *prop)
{
struct battery *battery;
int i;
int j;
if (prop) {
/* Single propety supplied */
battery = (struct battery *)battery_get_battery(prop->bp_bat_num);
for (i = 0; i < battery->b_listener_count; ++i) {
if (listener == battery->b_listeners[i].ld_listener) {
break;
}
}
if (i >= battery->b_listener_count) {
return -1;
}
clear_bit(battery->b_listeners[i].ld_prop_change_mask,
prop->bp_prop_num);
} else {
/* No property supplied, remove listener from all subscriptions */
for (i = 0; i < BATTERY_MAX_COUNT; ++i) {
battery = battery_manager.bm_batteries[i];
for (j = 0; j < battery->b_listener_count; ++i) {
if (battery->b_listeners[j].ld_listener == listener) {
/* Reduce number of listeners */
battery->b_listener_count--;
if (j < battery->b_listener_count) {
/* It was not last listener,
* move last to emptied space */
battery->b_listeners[j] =
battery->b_listeners[battery->b_listener_count];
}
battery->b_listeners =
realloc(battery->b_listeners,
sizeof(struct listener_data) *
battery->b_listener_count);
break;
}
}
}
}
battery_update_polled_properties(battery);
return 0;
}
int
battery_prop_poll_subscribe(struct battery_prop_listener *listener,
struct battery_property *prop)
{
struct battery *battery;
int listener_index;
assert(listener);
assert(prop);
battery = (struct battery *)battery_get_battery(prop->bp_bat_num);
listener_index = get_listener_index(battery, listener);
set_bit(battery->b_listeners[listener_index].ld_prop_read_mask,
prop->bp_prop_num);
battery_update_polled_properties(battery);
return 0;
}
int
battery_prop_poll_unsubscribe(struct battery_prop_listener *listener,
struct battery_property *prop)
{
struct battery *battery;
int i;
int j;
if (prop) {
battery = (struct battery *)battery_get_battery(prop->bp_bat_num);
for (i = 0; i < battery->b_listener_count; ++i) {
if (listener == battery->b_listeners[i].ld_listener) {
break;
}
}
if (i >= battery->b_listener_count) {
return -1;
}
clear_bit(battery->b_listeners[i].ld_prop_read_mask,
prop->bp_prop_num);
} else {
for (i = 0; i < BATTERY_MAX_COUNT; ++i) {
battery = battery_manager.bm_batteries[i];
for (j = 0; j < battery->b_listener_count; ++i) {
if (battery->b_listeners[j].ld_listener == listener) {
battery->b_listener_count--;
if (j < battery->b_listener_count) {
battery->b_listeners[j] =
battery->b_listeners[battery->b_listener_count];
}
battery->b_listeners =
realloc(battery->b_listeners,
sizeof(struct listener_data) *
battery->b_listener_count);
break;
}
}
}
}
battery_update_polled_properties(battery);
return 0;
}
static int
battery_open(struct os_dev *dev, uint32_t timeout, void *arg)
{
struct battery *bat = (struct battery *)dev;
struct os_dev *drv_dev;
int rollback = -1;
int i;
int rc = 0;
for (i = 0; i < BATTERY_DRIVERS_MAX && bat->b_drivers[i]; ++i) {
drv_dev = os_dev_open(bat->b_drivers[i]->dev.od_name, 0, NULL);
assert(drv_dev == &bat->b_drivers[i]->dev);
if (drv_dev != &bat->b_drivers[i]->dev) {
rollback = i - 1;
rc = -1;
break;
}
}
for (i = rollback; i >= 0; --i) {
os_dev_close(&bat->b_drivers[i]->dev);
}
return rc;
}
static int
battery_close(struct os_dev *dev)
{
struct battery *bat = (struct battery *)dev;
int i;
for (i = 0; i < BATTERY_DRIVERS_MAX && bat->b_drivers[i]; ++i) {
os_dev_close(&bat->b_drivers[i]->dev);
}
return 0;
}
int
battery_init(struct os_dev *dev, void *arg)
{
int i;
struct battery *bat = (struct battery *)dev;
OS_DEV_SETHANDLERS(dev, battery_open, battery_close);
for (i = 0; i < BATTERY_MAX_COUNT; ++i) {
if (battery_manager.bm_batteries[i] == NULL) {
break;
}
}
assert(i < BATTERY_MAX_COUNT);
battery_manager.bm_batteries[i] = bat;
memset(bat->b_drivers, 0, sizeof(bat->b_drivers));
return 0;
}