Merged in juniskane/nuttx_stm32l4/1wire_sht21_changes_pr (pull request #631)

drivers/1wire: add PM hooks and unlink support to sht21 sensor (allowing hot-swappable I2C bus from 1-wire converter)

* drivers/1wire: add PM hook to 1wire bus

* drivers/1wire/ds28e17.c: i2c instance must be zeroed

* drivers/sht21: add unlink support

    Unlike most other unlink implementations in NuttX drivers, this one
    does not allow any I2C operations after unlink, making it possible to
    deallocate the I2C bus.

Approved-by: Gregory Nutt <gnutt@nuttx.org>
This commit is contained in:
Juha Niskanen 2018-04-24 13:21:02 +00:00 committed by Gregory Nutt
parent d24ce35e1c
commit 3cf830bd67
4 changed files with 303 additions and 13 deletions

View File

@ -62,6 +62,15 @@
#define NO_HOLDER ((pid_t)-1) #define NO_HOLDER ((pid_t)-1)
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
#ifdef CONFIG_PM
static int onewire_pm_prepare(FAR struct pm_callback_s *cb, int domain,
enum pm_state_e pmstate);
#endif
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
@ -122,6 +131,87 @@ static inline void onewire_sem_init(FAR struct onewire_sem_s *sem)
nxsem_init(&sem->sem, 0, 1); nxsem_init(&sem->sem, 0, 1);
} }
/****************************************************************************
* Name: onewire_sem_destroy
*
* Description:
*
****************************************************************************/
static inline void onewire_sem_destroy(FAR struct onewire_sem_s *sem)
{
nxsem_destroy(&sem->sem);
}
/************************************************************************************
* Name: onewire_pm_prepare
*
* Description:
* Request the driver to prepare for a new power state. This is a
* warning that the system is about to enter into a new power state. The
* driver should begin whatever operations that may be required to enter
* power state. The driver may abort the state change mode by returning
* a non-zero value from the callback function.
*
* Input Parameters:
* cb - Returned to the driver. The driver version of the callback
* structure may include additional, driver-specific state
* data at the end of the structure.
* domain - Identifies the activity domain of the state change
* pmstate - Identifies the new PM state
*
* Returned Value:
* 0 (OK) means the event was successfully processed and that the driver
* is prepared for the PM state change. Non-zero means that the driver
* is not prepared to perform the tasks needed achieve this power setting
* and will cause the state change to be aborted. NOTE: The prepare
* method will also be recalled when reverting from lower back to higher
* power consumption modes (say because another driver refused a lower
* power state change). Drivers are not permitted to return non-zero
* values when reverting back to higher power consumption modes!
*
************************************************************************************/
#ifdef CONFIG_PM
static int onewire_pm_prepare(FAR struct pm_callback_s *cb, int domain,
enum pm_state_e pmstate)
{
struct onewire_master_s *master =
(struct onewire_master_s *)((char *)cb -
offsetof(struct onewire_master_s, pm_cb));
int sval;
/* Logic to prepare for a reduced power state goes here. */
switch (pmstate)
{
case PM_NORMAL:
case PM_IDLE:
break;
case PM_STANDBY:
case PM_SLEEP:
/* Check if exclusive lock for the bus master is held. */
if (nxsem_getvalue(&master->devsem.sem, &sval) < 0)
{
DEBUGASSERT(false);
return -EINVAL;
}
if (sval <= 0)
{
/* Exclusive lock is held, do not allow entry to deeper PM states. */
return -EBUSY;
}
break;
}
return OK;
}
#endif
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
@ -167,7 +257,7 @@ void onewire_sem_wait(FAR struct onewire_master_s *master)
} }
while (ret == -EINTR); while (ret == -EINTR);
/* No we hold the semaphore */ /* Now we hold the semaphore */
master->devsem.holder = me; master->devsem.holder = me;
master->devsem.count = 1; master->devsem.count = 1;
@ -636,5 +726,39 @@ FAR struct onewire_master_s *
master->maxslaves = maxslaves; master->maxslaves = maxslaves;
master->insearch = false; master->insearch = false;
#ifdef CONFIG_PM
master->pm_cb.prepare = onewire_pm_prepare;
/* Register to receive power management callbacks */
(void)pm_register(&master->pm_cb);
#endif
return master; return master;
} }
/****************************************************************************
* Name: onewire_uninitialize
*
* Description:
* Release 1-wire bus master.
*
* Input Parameters:
* master - Pointer to the allocated 1-wire master
*
****************************************************************************/
int onewire_uninitialize(FAR struct onewire_master_s *master)
{
#ifdef CONFIG_PM
/* Unregister power management callbacks */
pm_unregister(&master->pm_cb);
#endif
/* Release resources. This does not touch the underlying onewire_dev_s */
onewire_sem_destroy(&master->devsem);
kmm_free(master);
return OK;
}

View File

@ -44,6 +44,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <semaphore.h> #include <semaphore.h>
#include <nuttx/power/pm.h>
/**************************************************************************** /****************************************************************************
* Public Types * Public Types
@ -65,6 +66,9 @@ struct onewire_master_s
int nslaves; /* Number of 1-wire slaves */ int nslaves; /* Number of 1-wire slaves */
int maxslaves; /* Maximum number of 1-wire slaves */ int maxslaves; /* Maximum number of 1-wire slaves */
bool insearch; /* If we are in middle of 1-wire search */ bool insearch; /* If we are in middle of 1-wire search */
#ifdef CONFIG_PM
struct pm_callback_s pm_cb; /* PM callbacks */
#endif
}; };
struct onewire_slave_s struct onewire_slave_s
@ -177,4 +181,17 @@ int onewire_search(FAR struct onewire_master_s *master,
FAR struct onewire_master_s * FAR struct onewire_master_s *
onewire_initialize(FAR struct onewire_dev_s *dev, int maxslaves); onewire_initialize(FAR struct onewire_dev_s *dev, int maxslaves);
/****************************************************************************
* Name: onewire_uninitialize
*
* Description:
* Release 1-wire bus master.
*
* Input Parameters:
* master - Pointer to the allocated 1-wire master
*
****************************************************************************/
int onewire_uninitialize(FAR struct onewire_master_s *master);
#endif /* __DRIVERS_1WIRE_1WIRE_INTERNAL_H */ #endif /* __DRIVERS_1WIRE_1WIRE_INTERNAL_H */

View File

@ -765,7 +765,7 @@ FAR struct i2c_master_s *
/* Allocate instance */ /* Allocate instance */
inst = kmm_malloc(sizeof(struct ds_i2c_inst_s)); inst = kmm_zalloc(sizeof(struct ds_i2c_inst_s));
if (inst == NULL) if (inst == NULL)
{ {
i2cerr("ERROR: Failed to allocate instance\n"); i2cerr("ERROR: Failed to allocate instance\n");
@ -778,12 +778,12 @@ FAR struct i2c_master_s *
inst->master = master; inst->master = master;
inst->frequency = 400000; /* power-on frequency */ inst->frequency = 400000; /* power-on frequency */
inst->stretch = DS_DEFAULT_STRETCH; inst->stretch = DS_DEFAULT_STRETCH;
inst->slave.romcode = romcode;
/* We need a recursive lock as this may be called from a search callback. */ /* We need a recursive lock as this may be called from a search callback. */
onewire_sem_wait(master); onewire_sem_wait(master);
inst->slave.romcode = romcode;
if (onewire_addslave(master, &inst->slave) < 0) if (onewire_addslave(master, &inst->slave) < 0)
{ {
kmm_free(inst); kmm_free(inst);

View File

@ -84,9 +84,15 @@ struct sht21_dev_s
FAR struct i2c_master_s *i2c; /* I2C interface */ FAR struct i2c_master_s *i2c; /* I2C interface */
uint8_t addr; /* I2C address */ uint8_t addr; /* I2C address */
bool valid; /* If cached readings are valid */ bool valid; /* If cached readings are valid */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
bool unlinked; /* True, driver has been unlinked */
#endif
struct timespec last_update; /* Last time when sensor was read */ struct timespec last_update; /* Last time when sensor was read */
int temperature; /* Cached temperature */ int temperature; /* Cached temperature */
int humidity; /* Cached humidity */ int humidity; /* Cached humidity */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
int16_t crefs; /* Number of open references */
#endif
sem_t devsem; sem_t devsem;
}; };
@ -107,14 +113,19 @@ static int sht21_read16(FAR struct sht21_dev_s *priv, uint8_t regaddr,
/* Character driver methods */ /* Character driver methods */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
static int sht21_open(FAR struct file *filep); static int sht21_open(FAR struct file *filep);
static int sht21_close(FAR struct file *filep); static int sht21_close(FAR struct file *filep);
#endif
static ssize_t sht21_read(FAR struct file *filep, FAR char *buffer, static ssize_t sht21_read(FAR struct file *filep, FAR char *buffer,
size_t buflen); size_t buflen);
static ssize_t sht21_write(FAR struct file *filep, FAR const char *buffer, static ssize_t sht21_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen); size_t buflen);
static int sht21_ioctl(FAR struct file *filep, int cmd, static int sht21_ioctl(FAR struct file *filep, int cmd,
unsigned long arg); unsigned long arg);
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
static int sht21_unlink(FAR struct inode *inode);
#endif
/**************************************************************************** /****************************************************************************
* Private Data * Private Data
@ -122,8 +133,13 @@ static int sht21_ioctl(FAR struct file *filep, int cmd,
static const struct file_operations g_sht21fops = static const struct file_operations g_sht21fops =
{ {
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
sht21_open, /* open */ sht21_open, /* open */
sht21_close, /* close */ sht21_close, /* close */
#else
NULL, /* open */
NULL, /* close */
#endif
sht21_read, /* read */ sht21_read, /* read */
sht21_write, /* write */ sht21_write, /* write */
NULL, /* seek */ NULL, /* seek */
@ -132,7 +148,7 @@ static const struct file_operations g_sht21fops =
, NULL /* poll */ , NULL /* poll */
#endif #endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL /* unlink */ , sht21_unlink /* unlink */
#endif #endif
}; };
@ -362,10 +378,36 @@ static int sht21_read_values(FAR struct sht21_dev_s *priv, FAR int *temp,
* *
****************************************************************************/ ****************************************************************************/
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
static int sht21_open(FAR struct file *filep) static int sht21_open(FAR struct file *filep)
{ {
FAR struct inode *inode = filep->f_inode;
FAR struct sht21_dev_s *priv = inode->i_private;
int ret;
/* Get exclusive access */
do
{
ret = nxsem_wait(&priv->devsem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
/* Increment the count of open references on the driver */
priv->crefs++;
DEBUGASSERT(priv->crefs > 0);
nxsem_post(&priv->devsem);
return OK; return OK;
} }
#endif
/**************************************************************************** /****************************************************************************
* Name: sht21_close * Name: sht21_close
@ -375,10 +417,47 @@ static int sht21_open(FAR struct file *filep)
* *
****************************************************************************/ ****************************************************************************/
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
static int sht21_close(FAR struct file *filep) static int sht21_close(FAR struct file *filep)
{ {
FAR struct inode *inode = filep->f_inode;
FAR struct sht21_dev_s *priv = inode->i_private;
int ret;
/* Get exclusive access */
do
{
ret = nxsem_wait(&priv->devsem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
/* Decrement the count of open references on the driver */
DEBUGASSERT(priv->crefs > 0);
priv->crefs--;
/* If the count has decremented to zero and the driver has been unlinked,
* then free memory now.
*/
if (priv->crefs <= 0 && priv->unlinked)
{
nxsem_destroy(&priv->devsem);
kmm_free(priv);
return OK;
}
nxsem_post(&priv->devsem);
return OK; return OK;
} }
#endif
/**************************************************************************** /****************************************************************************
* Name: sht21_read * Name: sht21_read
@ -407,6 +486,18 @@ static ssize_t sht21_read(FAR struct file *filep, FAR char *buffer, size_t bufle
} }
while (ret == -EINTR); while (ret == -EINTR);
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
if (priv->unlinked)
{
/* Do not allow operations on unlinked sensors. This allows
* sensor use on hot swappable I2C bus.
*/
nxsem_post(&priv->devsem);
return -ENODEV;
}
#endif
ret = sht21_read_values(priv, &temp, &rh); ret = sht21_read_values(priv, &temp, &rh);
if (ret < 0) if (ret < 0)
{ {
@ -461,6 +552,18 @@ static int sht21_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
} }
while (ret == -EINTR); while (ret == -EINTR);
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
if (priv->unlinked)
{
/* Do not allow operations on unlinked sensors. This allows
* sensor use on hot swappable I2C bus.
*/
nxsem_post(&priv->devsem);
return -ENODEV;
}
#endif
switch (cmd) switch (cmd)
{ {
/* Soft reset the SHT2x, Arg: None */ /* Soft reset the SHT2x, Arg: None */
@ -514,6 +617,52 @@ static int sht21_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
return ret; return ret;
} }
/****************************************************************************
* Name: sht21_unlink
****************************************************************************/
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
static int sht21_unlink(FAR struct inode *inode)
{
FAR struct sht21_dev_s *priv;
int ret;
DEBUGASSERT(inode != NULL && inode->i_private != NULL);
priv = (FAR struct sht21_dev_s *)inode->i_private;
/* Get exclusive access */
do
{
ret = nxsem_wait(&priv->devsem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
/* Are there open references to the driver data structure? */
if (priv->crefs <= 0)
{
nxsem_destroy(&priv->devsem);
kmm_free(priv);
return OK;
}
/* No... just mark the driver as unlinked and free the resources when
* the last client closes their reference to the driver.
*/
priv->unlinked = true;
nxsem_post(&priv->devsem);
return ret;
}
#endif
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/