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)
|
#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;
|
||||||
|
}
|
||||||
|
@ -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 */
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
Loading…
Reference in New Issue
Block a user