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:
parent
d24ce35e1c
commit
3cf830bd67
@ -62,6 +62,15 @@
|
||||
|
||||
#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
|
||||
****************************************************************************/
|
||||
@ -122,6 +131,87 @@ static inline void onewire_sem_init(FAR struct onewire_sem_s *sem)
|
||||
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
|
||||
****************************************************************************/
|
||||
@ -167,7 +257,7 @@ void onewire_sem_wait(FAR struct onewire_master_s *master)
|
||||
}
|
||||
while (ret == -EINTR);
|
||||
|
||||
/* No we hold the semaphore */
|
||||
/* Now we hold the semaphore */
|
||||
|
||||
master->devsem.holder = me;
|
||||
master->devsem.count = 1;
|
||||
@ -636,5 +726,39 @@ FAR struct onewire_master_s *
|
||||
master->maxslaves = maxslaves;
|
||||
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;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
@ -44,6 +44,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <semaphore.h>
|
||||
#include <nuttx/power/pm.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
@ -65,6 +66,9 @@ struct onewire_master_s
|
||||
int nslaves; /* Number of 1-wire slaves */
|
||||
int maxslaves; /* Maximum number of 1-wire slaves */
|
||||
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
|
||||
@ -177,4 +181,17 @@ int onewire_search(FAR struct onewire_master_s *master,
|
||||
FAR struct onewire_master_s *
|
||||
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 */
|
||||
|
@ -765,7 +765,7 @@ FAR struct i2c_master_s *
|
||||
|
||||
/* Allocate instance */
|
||||
|
||||
inst = kmm_malloc(sizeof(struct ds_i2c_inst_s));
|
||||
inst = kmm_zalloc(sizeof(struct ds_i2c_inst_s));
|
||||
if (inst == NULL)
|
||||
{
|
||||
i2cerr("ERROR: Failed to allocate instance\n");
|
||||
@ -778,12 +778,12 @@ FAR struct i2c_master_s *
|
||||
inst->master = master;
|
||||
inst->frequency = 400000; /* power-on frequency */
|
||||
inst->stretch = DS_DEFAULT_STRETCH;
|
||||
inst->slave.romcode = romcode;
|
||||
|
||||
/* We need a recursive lock as this may be called from a search callback. */
|
||||
|
||||
onewire_sem_wait(master);
|
||||
|
||||
inst->slave.romcode = romcode;
|
||||
if (onewire_addslave(master, &inst->slave) < 0)
|
||||
{
|
||||
kmm_free(inst);
|
||||
|
@ -84,9 +84,15 @@ struct sht21_dev_s
|
||||
FAR struct i2c_master_s *i2c; /* I2C interface */
|
||||
uint8_t addr; /* I2C address */
|
||||
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 */
|
||||
int temperature; /* Cached temperature */
|
||||
int humidity; /* Cached humidity */
|
||||
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
||||
int16_t crefs; /* Number of open references */
|
||||
#endif
|
||||
sem_t devsem;
|
||||
};
|
||||
|
||||
@ -107,14 +113,19 @@ static int sht21_read16(FAR struct sht21_dev_s *priv, uint8_t regaddr,
|
||||
|
||||
/* Character driver methods */
|
||||
|
||||
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
||||
static int sht21_open(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,
|
||||
size_t buflen);
|
||||
static ssize_t sht21_write(FAR struct file *filep, FAR const char *buffer,
|
||||
size_t buflen);
|
||||
static int sht21_ioctl(FAR struct file *filep, int cmd,
|
||||
unsigned long arg);
|
||||
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
||||
static int sht21_unlink(FAR struct inode *inode);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
@ -122,17 +133,22 @@ static int sht21_ioctl(FAR struct file *filep, int cmd,
|
||||
|
||||
static const struct file_operations g_sht21fops =
|
||||
{
|
||||
sht21_open, /* open */
|
||||
sht21_close, /* close */
|
||||
sht21_read, /* read */
|
||||
sht21_write, /* write */
|
||||
NULL, /* seek */
|
||||
sht21_ioctl /* ioctl */
|
||||
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
||||
sht21_open, /* open */
|
||||
sht21_close, /* close */
|
||||
#else
|
||||
NULL, /* open */
|
||||
NULL, /* close */
|
||||
#endif
|
||||
sht21_read, /* read */
|
||||
sht21_write, /* write */
|
||||
NULL, /* seek */
|
||||
sht21_ioctl /* ioctl */
|
||||
#ifndef CONFIG_DISABLE_POLL
|
||||
, NULL /* poll */
|
||||
, NULL /* poll */
|
||||
#endif
|
||||
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
||||
, NULL /* unlink */
|
||||
, sht21_unlink /* unlink */
|
||||
#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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sht21_read
|
||||
@ -386,7 +465,7 @@ static int sht21_close(FAR struct file *filep)
|
||||
|
||||
static ssize_t sht21_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
|
||||
{
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
FAR struct sht21_dev_s *priv = inode->i_private;
|
||||
ssize_t length = 0;
|
||||
int temp;
|
||||
@ -407,6 +486,18 @@ static ssize_t sht21_read(FAR struct file *filep, FAR char *buffer, size_t bufle
|
||||
}
|
||||
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);
|
||||
if (ret < 0)
|
||||
{
|
||||
@ -444,7 +535,7 @@ static ssize_t sht21_write(FAR struct file *filep, FAR const char *buffer,
|
||||
static int sht21_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
||||
{
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
FAR struct sht21_dev_s *priv = inode->i_private;
|
||||
FAR struct sht21_dev_s *priv = inode->i_private;
|
||||
int ret;
|
||||
|
||||
/* Get exclusive access */
|
||||
@ -461,6 +552,18 @@ static int sht21_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
||||
}
|
||||
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)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* 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
|
||||
****************************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user