diff --git a/drivers/power/battery_monitor.c b/drivers/power/battery_monitor.c index 585e399b80e..a4cacad70aa 100644 --- a/drivers/power/battery_monitor.c +++ b/drivers/power/battery_monitor.c @@ -28,10 +28,14 @@ #include #include +#include #include +#include +#include #include #include +#include #include #include @@ -47,9 +51,18 @@ ****************************************************************************/ /**************************************************************************** - * Private + * Private type ****************************************************************************/ +struct battery_monitor_priv_s +{ + struct list_node node; + sem_t lock; + sem_t wait; + uint32_t mask; + FAR struct pollfd *fds; +}; + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -59,11 +72,13 @@ static int bat_monitor_open(FAR struct file *filep); static int bat_monitor_close(FAR struct file *filep); static ssize_t bat_monitor_read(FAR struct file *filep, FAR char *buffer, - size_t buflen); + size_t buflen); static ssize_t bat_monitor_write(FAR struct file *filep, - FAR const char *buffer, size_t buflen); + FAR const char *buffer, size_t buflen); static int bat_monitor_ioctl(FAR struct file *filep, int cmd, - unsigned long arg); + unsigned long arg); +static int bat_monitor_poll(FAR struct file *filep, + FAR struct pollfd *fds, bool setup); /**************************************************************************** * Private Data @@ -77,13 +92,48 @@ static const struct file_operations g_batteryops = bat_monitor_write, NULL, bat_monitor_ioctl, - NULL + bat_monitor_poll }; /**************************************************************************** * Private Functions ****************************************************************************/ +static int battery_monitor_notify(FAR struct battery_monitor_priv_s *priv, + uint32_t mask) +{ + FAR struct pollfd *fd = priv->fds; + int semcnt; + int ret; + + ret = nxsem_wait_uninterruptible(&priv->lock); + if (ret < 0) + { + return ret; + } + + priv->mask |= mask; + if (priv->mask) + { + fd->revents |= POLLIN; + nxsem_get_value(fd->sem, &semcnt); + if (semcnt < 1) + { + nxsem_post(fd->sem); + } + + nxsem_get_value(&priv->wait, &semcnt); + if (semcnt < 1) + { + nxsem_post(&priv->wait); + } + } + + nxsem_post(&priv->lock); + + return OK; +} + /**************************************************************************** * Name: bat_monitor_open * @@ -94,7 +144,31 @@ static const struct file_operations g_batteryops = static int bat_monitor_open(FAR struct file *filep) { - return OK; + FAR struct battery_monitor_priv_s *priv; + FAR struct battery_monitor_dev_s *dev = filep->f_inode->i_private; + int ret; + + priv = kmm_zalloc(sizeof(*priv)); + if (priv == NULL) + { + return -ENOMEM; + } + + ret = nxsem_wait(&dev->batsem); + if (ret < 0) + { + kmm_free(priv); + return ret; + } + + nxsem_init(&priv->lock, 0, 1); + nxsem_init(&priv->wait, 0, 0); + nxsem_set_protocol(&priv->wait, SEM_PRIO_NONE); + list_add_tail(&dev->flist, &priv->node); + nxsem_post(&dev->batsem); + filep->f_priv = priv; + + return ret; } /**************************************************************************** @@ -107,6 +181,22 @@ static int bat_monitor_open(FAR struct file *filep) static int bat_monitor_close(FAR struct file *filep) { + FAR struct battery_monitor_priv_s *priv = filep->f_priv; + FAR struct battery_monitor_dev_s *dev = filep->f_inode->i_private; + int ret; + + ret = nxsem_wait(&dev->batsem); + if (ret < 0) + { + return ret; + } + + list_delete(&priv->node); + nxsem_post(&dev->batsem); + nxsem_destroy(&priv->lock); + nxsem_destroy(&priv->wait); + kmm_free(priv); + return OK; } @@ -117,9 +207,46 @@ static int bat_monitor_close(FAR struct file *filep) static ssize_t bat_monitor_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { - /* Return nothing read */ + FAR struct battery_monitor_priv_s *priv = filep->f_priv; + int ret; - return 0; + if (buflen < sizeof(priv->mask)) + { + return -EINVAL; + } + + ret = nxsem_wait(&priv->lock); + if (ret < 0) + { + return ret; + } + + while (priv->mask == 0) + { + nxsem_post(&priv->lock); + if (filep->f_oflags & O_NONBLOCK) + { + return -EAGAIN; + } + + ret = nxsem_wait(&priv->wait); + if (ret < 0) + { + return ret; + } + + ret = nxsem_wait(&priv->lock); + if (ret < 0) + { + return ret; + } + } + + memcpy(buffer, &priv->mask, sizeof(priv->mask)); + priv->mask = 0; + + nxsem_post(&priv->lock); + return sizeof(priv->mask); } /**************************************************************************** @@ -310,10 +437,76 @@ static int bat_monitor_ioctl(FAR struct file *filep, int cmd, return ret; } +/**************************************************************************** + * Name: bat_monitor_poll + ****************************************************************************/ + +static ssize_t bat_monitor_poll(FAR struct file *filep, + struct pollfd *fds, bool setup) +{ + FAR struct battery_monitor_priv_s *priv = filep->f_priv; + int ret; + + ret = nxsem_wait(&priv->lock); + if (ret < 0) + { + return ret; + } + + if (setup) + { + if (priv->fds == NULL) + { + priv->fds = fds; + fds->priv = &priv->fds; + } + else + { + ret = -EBUSY; + } + } + else if (fds->priv != NULL) + { + priv->fds = NULL; + fds->priv = NULL; + } + + nxsem_post(&priv->lock); + + if (setup) + { + battery_monitor_notify(priv, 0); + } + + return ret; +} + /**************************************************************************** * Public Functions ****************************************************************************/ +int battery_monitor_changed(FAR struct battery_monitor_dev_s *dev, + uint32_t mask) +{ + FAR struct battery_monitor_priv_s *priv; + int ret; + + ret = nxsem_wait_uninterruptible(&dev->batsem); + if (ret < 0) + { + return ret; + } + + list_for_every_entry(&dev->flist, priv, + struct battery_monitor_priv_s, node) + { + battery_monitor_notify(priv, mask); + } + + nxsem_post(&dev->batsem); + return OK; +} + /**************************************************************************** * Name: battery_monitor_register * @@ -336,9 +529,10 @@ int battery_monitor_register(FAR const char *devpath, { int ret; - /* Initialize the semaphore */ + /* Initialize the semaphore and the list */ nxsem_init(&dev->batsem, 0, 1); + list_initialize(&dev->flist); /* Register the character driver */ diff --git a/include/nuttx/power/battery_ioctl.h b/include/nuttx/power/battery_ioctl.h index edb1318ec7f..298aa2b6196 100644 --- a/include/nuttx/power/battery_ioctl.h +++ b/include/nuttx/power/battery_ioctl.h @@ -69,7 +69,9 @@ #define BATTERY_VOLTAGE_CHANGED (1U << 3) #define BATTERY_CURRENT_CHANGED (1U << 4) #define BATTERY_CAPACITY_CHANGED (1U << 5) -#define BATTERY_TEMPERATURE_CHANGED (1U << 6) +#define BATTERY_CELLVOLTAGE_CHANGED (1U << 6) +#define BATTERY_TEMPERATURE_CHANGED (1U << 7) +#define BATTERY_COULOMBS_CHANGED (1U << 8) /**************************************************************************** * Public Types diff --git a/include/nuttx/power/battery_monitor.h b/include/nuttx/power/battery_monitor.h index 8239c6cdd00..6f147403997 100644 --- a/include/nuttx/power/battery_monitor.h +++ b/include/nuttx/power/battery_monitor.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -296,6 +297,8 @@ struct battery_monitor_dev_s FAR const struct battery_monitor_operations_s *ops; /* Battery operations */ sem_t batsem; /* Enforce mutually exclusive access */ + struct list_node flist; + /* Data fields specific to the lower-half driver may follow */ }; @@ -317,6 +320,13 @@ extern "C" * Public Function Prototypes ****************************************************************************/ +/**************************************************************************** + * Name: battery_monitor_changed + ****************************************************************************/ + +int battery_monitor_changed(FAR struct battery_monitor_dev_s *dev, + uint32_t mask); + /**************************************************************************** * Name: battery_monitor_register *