nuttx/drivers/sensors/lsm330_spi.c
Xiang Xiao 5c80b94820 Replace #include <semaphore.h> to #include <nuttx/semaphore.h>
Since the kernel side should call nxsem_xxx instead and remove the unused inclusion
2020-02-01 08:27:30 -06:00

1431 lines
44 KiB
C

/****************************************************************************
* drivers/sensors/lsm330.c
* Character driver for the ST LSM330 Tri-axis accelerometer and gyroscope.
*
* Copyright (C) 2017-2018 RAF Research LLC. All rights reserved.
* Author: Bob Feretich <bob.feretich@rafresearch.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright+
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#if defined(CONFIG_SPI) && defined(CONFIG_SENSORS_LSM330SPI) \
&& defined(CONFIG_SPI_EXCHANGE)
#include <errno.h>
#include <debug.h>
#include <string.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/semaphore.h>
#include <nuttx/sensors/lsm330.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define LSM330_INITIAL_ACLCR_SIZE 7
#define LSM330_INITIAL_GYROCR_SIZE 5
/****************************************************************************
* Private structure definitions
****************************************************************************/
struct sensor_data_s
{
int16_t x_gyr; /* Measurement result for x axis */
int16_t y_gyr; /* Measurement result for y axis */
int16_t z_gyr; /* Measurement result for z axis */
};
struct lsm330_dev_s
{
FAR struct lsm330_dev_s *flink; /* Supports a singly linked list of
* drivers */
FAR struct spi_dev_s *spi; /* Pointer to the SPI instance */
FAR struct lsm330_config_s *config; /* Pointer to the configuration of the
* LSM330 sensor */
sem_t devicesem; /* Manages exclusive access to this
* device */
sem_t datasem; /* Manages exclusive access to this
* structure */
struct sensor_data_s data; /* The data as measured by the sensor */
uint8_t seek_address; /* Current device address. */
uint8_t readonly; /* 0 = writing to the device in enabled */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static uint8_t lsm330_read_register(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr);
static void lsm330_read_acl_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt);
static void lsm330_read_gyro_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt);
static void lsm330_write_register(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
uint8_t reg_data);
static void lsm330_write_acl_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt);
static void lsm330_write_gyro_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt);
static void lsm330acl_reset(FAR struct lsm330_dev_s *dev);
static void lsm330gyro_reset(FAR struct lsm330_dev_s *dev);
static int lsm330acl_open(FAR struct file *filep);
static int lsm330gyro_open(FAR struct file *filep);
static int lsm330acl_close(FAR struct file *filep);
static int lsm330gyro_close(FAR struct file *filep);
static ssize_t lsm330acl_read(FAR struct file *, FAR char *, size_t);
static ssize_t lsm330gyro_read(FAR struct file *, FAR char *, size_t);
static ssize_t lsm330acl_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static ssize_t lsm330gyro_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static off_t lsm330acl_seek(FAR struct file *filep, off_t offset,
int whence);
static off_t lsm330gyro_seek(FAR struct file *filep, off_t offset,
int whence);
static int lsm330_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
static int lsm330acl_dvr_open(FAR void *instance_handle, int32_t arg);
static int lsm330acl_dvr_close(FAR void *instance_handle, int32_t arg);
static int lsm330gyro_dvr_open(FAR void *instance_handle, int32_t arg);
static int lsm330gyro_dvr_close(FAR void *instance_handle,
int32_t arg);
static ssize_t lsm330acl_dvr_read(FAR void *instance_handle,
FAR char *buffer, size_t buflen);
static ssize_t lsm330gyro_dvr_read(FAR void *instance_handle,
FAR char *buffer, size_t buflen);
static ssize_t lsm330acl_dvr_write(FAR void *instance_handle,
FAR const char *buffer, size_t buflen);
static ssize_t lsm330gyro_dvr_write(FAR void *instance_handle,
FAR const char *buffer, size_t buflen);
static off_t lsm330acl_dvr_seek(FAR void *instance_handle, off_t offset,
int whence);
static off_t lsm330gyro_dvr_seek(FAR void *instance_handle, off_t offset,
int whence);
static int lsm330_dvr_ioctl(FAR void *instance_handle, int cmd,
unsigned long arg);
static void lsm330_dvr_exchange(FAR void *instance_handle,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_lsm330a_fops =
{
lsm330acl_open,
lsm330acl_close,
lsm330acl_read,
lsm330acl_write,
lsm330acl_seek,
lsm330_ioctl,
NULL
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL
#endif
};
static const struct file_operations g_lsm330g_fops =
{
lsm330gyro_open,
lsm330gyro_close,
lsm330gyro_read,
lsm330gyro_write,
lsm330gyro_seek,
lsm330_ioctl,
NULL
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL
#endif
};
static const struct lsm330spi_dvr_entry_vector_s g_lsm330acl_dops =
{
{ /* Standard sensor cluster driver entry-vector */
.driver_open = lsm330acl_dvr_open,
.driver_close = lsm330acl_dvr_close,
.driver_read = lsm330acl_dvr_read,
.driver_write = lsm330acl_dvr_write,
.driver_seek = lsm330acl_dvr_seek,
.driver_ioctl = lsm330_dvr_ioctl,
.driver_suspend = 0,
.driver_resume = 0,
},
/* lsm330 extensions follow */
.driver_spiexc = lsm330_dvr_exchange,
};
static const struct lsm330spi_dvr_entry_vector_s g_lsm330gyro_dops =
{
{ /* Standard sensor cluster driver entry-vector */
.driver_open = lsm330gyro_dvr_open,
.driver_close = lsm330gyro_dvr_close,
.driver_read = lsm330gyro_dvr_read,
.driver_write = lsm330gyro_dvr_write,
.driver_seek = lsm330gyro_dvr_seek,
.driver_ioctl = lsm330_dvr_ioctl,
.driver_suspend = 0,
.driver_resume = 0,
},
/* lsm330 extensions follow */
.driver_spiexc = lsm330_dvr_exchange,
};
/* Single linked list to store instances of drivers */
static struct lsm330_dev_s *g_lsm330a_list = NULL;
static struct lsm330_dev_s *g_lsm330g_list = NULL;
/* Default accelerometer initialization sequence:
*
* Configure LSM330 to measure mode.
*
* 1. CR5: ODR=0, power-off
* 2. CR6: Bandwidth=800Hz. +/-16g range. 4-wire SPI.
* 3. CR7: Make sure auto-increment is turned on.
* 4. CR2: Zero state machine 1.
* 5. CR3: Zero state machine 2.
* 6. CR4: DataReady interrupt disabled.
* 7. CR5: Power up. 1600Hz sample rate. X,Y, & Z enabled.
*/
static struct lsm330_reg_pair_s g_default_lsm330_aclcr_values[] =
{
/* CR5 ODR[3:0] BDU ZEN YEN XEN */
/* 0000=Off 0 0 0 0=all disabled */
{
.addr = LSM330_ACL_CTRL_REG5,
.value = 0x00
},
/* CR6 BW[2:1] FSCALE[2:0] - - SIM */
/* 00=800Hz 10 0=16g 0 0 0=4-wire */
{
.addr = LSM330_ACL_CTRL_REG6,
.value = 0x20
},
/* CR7 BOOT FIFO_EN WTM_EN ADD_INC P1_MTY P1_WTM P1_OVR WTM_EN*/
/* 0 0 0 1 0 0 0 0 */
{
.addr = LSM330_ACL_CTRL_REG7,
.value = 0x10
},
/* CR2 HYST1 - SM1_PIN - - SM1_EN */
/* 000 0 0 0 0 0 */
{
.addr = LSM330_ACL_CTRL_REG2,
.value = 0x00
},
/* CR3 HYST2 - SM2_PIN - - SM2_EN */
/* 000 0 0 0 0 0 */
{
.addr = LSM330_ACL_CTRL_REG3,
.value = 0x00
},
/* CR4 DR_EN IEA IEL INT2_EN INT1_EN VFILT STRT */
/* 1 1 0 0 0 0 0 */
{
.addr = LSM330_ACL_CTRL_REG4,
.value = 0xc0
},
/* CR5 ODR[3:0] BDU ZEN YEN XEN */
/* 1001=1600Hz 1 1 1 1=all enabled */
{
.addr = LSM330_ACL_CTRL_REG5,
.value = 0x9f
}
};
/* Default gyroscope initialization sequence
*
* Configure LSM330 Gyroscope to measure mode.
*
* 1. CR1: Power up. 760Hz sample rate. Bandwidth=100Hz. X,Y, & Z enabled.
* 2. CR2: Normal measurement mode. High pass filter OFF.
* 3. CR3: All interrupts disabled.
* 4. CR4: +/-500dps range. Block update. 4-wire SPI.
* 5. CR5: Select Low Pass Filter 1 outputs. LPF2 bypassed.
*/
static struct lsm330_reg_pair_s g_default_lsm330_gyrocr_values[] =
{
/* CR1 DR[1:0] BW[1:0] PD Zen Xen Yen */
/* 1 1=760Hz 1 1=100Hz 1 1 1 1 */
{
.addr = LSM330_GYRO_CTRL_REG1,
.value = 0xff
},
/* CR2 EXTRen LVLen HPM[1:0] HPCF[3:0] */
/* 0 0 00=Normal xxxx Default */
{
.addr = LSM330_GYRO_CTRL_REG2,
.value = 0x00
},
/* CR3 I1_Int1 I1_Boot H_Lactive PP_OD I2_DRDY I2_WTM I2_ORun I2_Empty */
/* 0 0 0 0 0 0 0 0 */
{
.addr = LSM330_GYRO_CTRL_REG3,
.value = 0x00
},
/* CR4 BDU BLE FS[1:0] 0 0 0 SIM */
/* 1 0 01=500dps 0 0 0 0=4-wire */
{
.addr = LSM330_GYRO_CTRL_REG4,
.value = 0x90
},
/* CR5 BOOT FIFO_EN - HPen INT1_Sel[1:0] Out_Sel[1:0] */
/* 0 0 0 0 00=LPF1 00=LPF1 */
{
.addr = LSM330_GYRO_CTRL_REG5,
.value = 0x0a
}
};
/******************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: lsm330_read_register
****************************************************************************/
static uint8_t lsm330_read_register(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr)
{
uint8_t reg_data;
/* Lock the SPI bus so that only one device can access it at the same time */
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low to select the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address from where we want to read. */
SPI_SEND(dev->spi, reg_addr | LSM330_READ);
/* Write an idle byte while receiving the requested data */
reg_data = (uint8_t) (SPI_SEND(dev->spi, 0xff));
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
return reg_data;
}
/******************************************************************************
* Name: lsm330_read_acl_registerblk
******************************************************************************/
static void lsm330_read_acl_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt)
{
/* Lock the SPI bus so that only one device can access it at the same time */
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low to select the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address from where we want to start reading */
SPI_SEND(dev->spi, reg_addr | LSM330_READ);
/* Write idle bytes while receiving the requested data */
while (0 != xfercnt--)
{
*reg_data++ = (uint8_t)SPI_SEND(dev->spi, 0xff);
}
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/******************************************************************************
* Name: lsm330_read_gyro_registerblk
******************************************************************************/
static void lsm330_read_gyro_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt)
{
/* Lock the SPI bus so that only one device can access it at the same time */
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low to select the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address from where we want to start reading */
SPI_SEND(dev->spi, reg_addr | LSM330_READ |
(xfercnt > 1 ? LSM330_GYRO_AUTO : 0));
/* Write idle bytes while receiving the requested data */
while (0 != xfercnt--)
{
*reg_data++ = (uint8_t)SPI_SEND(dev->spi, 0xff);
}
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: lsm330_write_register
****************************************************************************/
static void lsm330_write_register(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
uint8_t reg_data)
{
/* Lock the SPI bus so that only one device can access it at the same time */
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low to select the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address to where we want to write */
SPI_SEND(dev->spi, reg_addr | LSM330_WRITE);
/* Transmit the content which should be written into the register */
SPI_SEND(dev->spi, reg_data);
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: lsm330_write_acl_registerblk
****************************************************************************/
static void lsm330_write_acl_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt)
{
/* Lock the SPI bus so that only one device can access it at the same time */
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low which selects the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address to where we want to start writing */
SPI_SEND(dev->spi, reg_addr | LSM330_WRITE);
/* Transmit the content which should be written in the register block */
while (0 != xfercnt--)
{
SPI_SEND(dev->spi, *reg_data++);
}
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: lsm330_write_gyro_registerblk
****************************************************************************/
static void lsm330_write_gyro_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt)
{
/* Lock the SPI bus so that only one device can access it at the same time */
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low which selects the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address to where we want to start writing */
SPI_SEND(dev->spi, reg_addr | LSM330_WRITE |
(xfercnt > 1 ? LSM330_GYRO_AUTO : 0));
/* Transmit the content which should be written in the register block */
while (0 != xfercnt--)
{
SPI_SEND(dev->spi, *reg_data++);
}
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: lsm330acl_reset
****************************************************************************/
static void lsm330acl_reset(FAR struct lsm330_dev_s *dev)
{
/* Reset LSM330 Accelerometer. Write only. Begin a boot.
* Note that the LSM330 ACL does not set the BOOT bit for read, so we
* can't loop on it.
*/
lsm330_write_register(dev, LSM330_ACL_CTRL_REG7, LSM330_ACR7_BOOT);
/* Wait for boot to finish */
up_mdelay(20);
/* Set auto-increment.
*
* CR7 BOOT FIFO_EN WTM_EN ADD_INC P1_MTY P1_WTM P1_OVR WTM_EN
* 0 0 0 1 0 0 0 0
*/
lsm330_write_register(dev, LSM330_ACL_CTRL_REG7, 0x10);
}
/****************************************************************************
* Name: lsm330gyro_reset
****************************************************************************/
static void lsm330gyro_reset(FAR struct lsm330_dev_s *dev)
{
/* Reset LSM330 Gyroscope. Write only. Begin a boot.
* Note that the LSM330 ACL does not set the BOOT bit for read, so we
* can't loop on it.
*/
lsm330_write_register(dev, LSM330_GYRO_CTRL_REG5, LSM_GYRO_BOOT_MASK);
/* Wait for boot to finish */
up_mdelay(20);
}
/****************************************************************************
* Name: lsm330acl_dvr_open
****************************************************************************/
static int lsm330acl_dvr_open(FAR void *instance_handle, int32_t arg)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
FAR struct lsm330_reg_pair_s *initp;
uint8_t reg_content;
int ret;
int sz;
int i;
sninfo("lsm330acl_open: entered...\n");
DEBUGASSERT(priv != NULL);
UNUSED(arg);
ret = nxsem_trywait(&priv->devicesem);
if (ret < 0)
{
sninfo("INFO: LSM330 Accelerometer is already open.\n");
set_errno(-EBUSY);
return -1;
}
/* Read the ID Register */
priv->readonly = false;
reg_content = lsm330_read_register(priv, LSM330_ACL_IDREG);
sninfo("LSM330_ACL_IDREG = 0x%02x\n", reg_content);
if (reg_content != LSM330_ACL_IDREG_VALUE)
{
/* Made info log level to permit open being used as a device probe. */
snwarn("INFO: "
"Device ID (0x%02X) does not match expected LSM330 Acl ID (0x%02).\n",
reg_content, LSM330_ACL_IDREG_VALUE);
set_errno(ENODEV);
priv->readonly = true;
}
else /* ID matches */
{
lsm330acl_reset(priv); /* Perform a sensor reset */
/* Choose the initialization sequence */
if (priv->config->initial_cr_values_size == 0 ||
priv->config->initial_cr_values == NULL)
{
initp = g_default_lsm330_aclcr_values; /* Default values */
sz = LSM330_INITIAL_ACLCR_SIZE;
sninfo("Using default CRs\n");
}
else
{
initp = priv->config->initial_cr_values; /* User supplied values */
sz = priv->config->initial_cr_values_size;
sninfo("Using provided CRs\n");
}
/* Apply the initialization sequence */
for (i = 0; i < sz; i++)
{
lsm330_write_register(priv, initp[i].addr, initp[i].value);
}
#ifdef CONFIG_DEBUG_SENSORS_INFO
/* Read back the content of all control registers for debug purposes */
reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG5);
sninfo("LSM330_ACL_CTRL_REG5 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG7);
sninfo("LSM330_ACL_CTRL_REG7 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG6);
sninfo("LSM330_ACL_CTRL_REG6 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG4);
sninfo("LSM330_ACL_CTRL_REG4 = 0x%02x\n", reg_content);
#endif
}
priv->seek_address = (uint8_t) LSM330_ACL_OUT_X_L;
return OK;
}
/****************************************************************************
* Name: lsm330gyro_dvr_open
****************************************************************************/
static int lsm330gyro_dvr_open(FAR void *instance_handle, int32_t arg)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
FAR struct lsm330_reg_pair_s *initp;
uint8_t reg_content;
int ret;
int sz;
int i;
sninfo("lsm330gyro_open: entered...\n");
DEBUGASSERT(priv != NULL);
UNUSED(arg);
ret = nxsem_trywait(&priv->devicesem);
if (ret < 0)
{
sninfo("INFO: LSM330 Gyroscope is already open.\n");
set_errno(-EBUSY);
return -1;
}
/* Read the ID Register */
priv->readonly = false;
reg_content = lsm330_read_register(priv, LSM330_GYRO_IDREG);
sninfo("LSM330_GYRO_IDREG = 0x%02x\n", reg_content);
if (reg_content != LSM330_GYRO_IDREG_VALUE)
{
/* Made warning log level to permit open being used as a device probe. */
snwarn("INFO: "
"Device ID (0x%02X) does not match expected LSM330 Gyro ID (0x%02).\n",
reg_content, LSM330_GYRO_IDREG_VALUE);
set_errno(ENODEV);
priv->readonly = true;
}
else /* ID matches */
{
lsm330gyro_reset(priv); /* Perform a sensor reset */
/* Choose the initialization sequence */
if (priv->config->initial_cr_values_size == 0 ||
priv->config->initial_cr_values == NULL)
{
initp = g_default_lsm330_gyrocr_values; /* Default values */
sz = LSM330_INITIAL_GYROCR_SIZE;
}
else
{
initp = priv->config->initial_cr_values; /* User supplied values */
sz = priv->config->initial_cr_values_size;
}
/* Apply the initialization sequence */
for (i = 0; i < sz; i++)
{
lsm330_write_register(priv, initp[i].addr, initp[i].value);
}
#ifdef CONFIG_DEBUG_SENSORS_INFO
/* Read back the content of all control registers for debug purposes */
reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG1);
sninfo("LSM330_GYRO_CTRL_REG1 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG2);
sninfo("LSM330_GYRO_CTRL_REG2 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG3);
sninfo("LSM330_GYRO_CTRL_REG3 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG4);
sninfo("LSM330_GYRO_CTRL_REG4 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG5);
sninfo("LSM330_GYRO_CTRL_REG5 = 0x%02x\n", reg_content);
#endif
}
priv->seek_address = (uint8_t) LSM330_GYRO_OUT_X_L;
return OK;
}
/****************************************************************************
* Name: lsm330acl_dvr_close
******************************************************************************/
static int lsm330acl_dvr_close(FAR void *instance_handle, int32_t arg)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
UNUSED(arg);
/* Perform a reset to put the sensor in standby mode. */
lsm330acl_reset(priv);
/* Release the sensor */
nxsem_post(&priv->devicesem);
return OK;
}
/****************************************************************************
* Name: lsm330gyro_dvr_close
******************************************************************************/
static int lsm330gyro_dvr_close(FAR void *instance_handle, int32_t arg)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
sninfo("lsm330gyro_close: entered...\n");
DEBUGASSERT(priv != NULL);
UNUSED(arg);
/* Perform a reset to put the sensor in standby mode. */
lsm330gyro_reset(priv);
/* Release the sensor */
nxsem_post(&priv->devicesem);
return OK;
}
/****************************************************************************
* Name: lsm330acl_dvr_read
****************************************************************************/
static ssize_t lsm330acl_dvr_read(FAR void *instance_handle,
FAR char *buffer, size_t buflen)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
lsm330_read_acl_registerblk(priv, priv->seek_address, (uint8_t *)buffer,
buflen);
return buflen;
}
/****************************************************************************
* Name: lsm330gyro_dvr_read
****************************************************************************/
static ssize_t lsm330gyro_dvr_read(FAR void *instance_handle, FAR char *buffer,
size_t buflen)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
lsm330_read_gyro_registerblk(priv, priv->seek_address, (uint8_t *)buffer,
buflen);
return buflen;
}
/****************************************************************************
* Name: lsm330acl_dvr_write
****************************************************************************/
static ssize_t lsm330acl_dvr_write(FAR void *instance_handle,
FAR const char *buffer, size_t buflen)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
if (priv->readonly)
{
set_errno(-EROFS);
return -1;
}
lsm330_write_acl_registerblk(priv, priv->seek_address, (uint8_t *)buffer,
buflen);
return buflen;
}
/****************************************************************************
* Name: lsm330gyro_dvr_write
****************************************************************************/
static ssize_t lsm330gyro_dvr_write(FAR void *instance_handle,
FAR const char *buffer, size_t buflen)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
if (priv->readonly)
{
set_errno(-EROFS);
return -1;
}
lsm330_write_gyro_registerblk(priv, priv->seek_address,
(FAR uint8_t *)buffer, buflen);
return buflen;
}
/****************************************************************************
* Name: lsm330acl_dvr_seek
****************************************************************************/
static off_t lsm330acl_dvr_seek(FAR void *instance_handle, off_t offset, int whence)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
off_t reg;
DEBUGASSERT(priv != NULL);
switch (whence)
{
case SEEK_CUR: /* incremental seek */
reg = priv->seek_address + offset;
if (0 > reg || reg > LSM330_ACL_LAST)
{
set_errno(-EINVAL);
return -1;
}
priv->seek_address = reg;
break;
case SEEK_END: /* seek to the 1st X-data register */
priv->seek_address = LSM330_ACL_OUT_X_L;
break;
case SEEK_SET: /* seek to designated address */
if (0 > offset || offset > LSM330_ACL_LAST)
{
set_errno(-EINVAL);
return -1;
}
priv->seek_address = offset;
break;
default: /* Invalid whence */
set_errno(-EINVAL);
return -1;
}
return priv->seek_address;
}
/****************************************************************************
* Name: lsm330gyro_dvr_seek
****************************************************************************/
static off_t lsm330gyro_dvr_seek(FAR void *instance_handle, off_t offset,
int whence)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
off_t reg;
DEBUGASSERT(priv != NULL);
switch (whence)
{
case SEEK_CUR: /* incremental seek */
reg = priv->seek_address + offset;
if (0 > reg || reg > LSM330_GYRO_LAST)
{
set_errno(-EINVAL);
return -1;
}
priv->seek_address = reg;
break;
case SEEK_END: /* seek to the 1st data register */
priv->seek_address = LSM330_GYRO_OUT_X_L;
break;
case SEEK_SET: /* seek to designated address */
if (0 > offset || offset > LSM330_GYRO_LAST)
{
set_errno(-EINVAL);
return -1;
}
priv->seek_address = offset;
break;
default: /* Invalid whence */
set_errno(-EINVAL);
return -1;
}
return priv->seek_address;
}
/****************************************************************************
* Name: lsm330_dvr_ioctl
****************************************************************************/
static int lsm330_dvr_ioctl(FAR void *instance_handle, int cmd,
unsigned long arg)
{
int ret = OK;
switch (cmd)
{
/* Command was not recognized */
default:
snerr("ERROR: Unrecognized cmd: %d\n", cmd);
ret = -ENOTTY;
break;
}
return ret;
}
/****************************************************************************
* Name: lsm330_dvr_exchange (with SPI DMA capability)
*
* Description:
* Exchange a block of data on SPI using DMA
*
* Input Parameters:
* instance_handle - Pointer to struct lsm330_dev_s.
* txbuffer - A pointer to the buffer of data to be sent
* rxbuffer - A pointer to a buffer in which to receive data
* nwords - the length of data to be exchanged in units of words.
* The wordsize is determined by the number of bits-per-word
* selected for the SPI interface. If nbits <= 8, the data is
* packed into uint8_t's; if nbits >8, the data is packed into
* uint16_t's
*
* Returned Value:
* None
*
******************************************************************************/
static void lsm330_dvr_exchange(FAR void *instance_handle,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
FAR struct spi_dev_s *spi = priv->spi;
sninfo("In lsm330_dvr_exchange: Handle=0x%08X\n", instance_handle);
/* Lock the SPI bus so that only one device can access it at the same time */
SPI_LOCK(spi, true);
SPI_SETFREQUENCY(spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(spi, LSM330_SPI_MODE);
sninfo("Calling SPI_EXCHANGE: devid=0x%08X\n", priv->config->spi_devid);
/* Set CS to low which selects the LSM330 */
SPI_SELECT(spi, priv->config->spi_devid, true);
/* Perform an SPI exchange block operation. */
SPI_EXCHANGE(spi, txbuffer, rxbuffer, nwords);
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(spi, priv->config->spi_devid, false);
sninfo("Returned from : SPI_EXCHANGE\n");
/* Unlock the SPI bus */
SPI_LOCK(spi, false);
}
/****************************************************************************
* Name: lsm330acl_open
****************************************************************************/
static int lsm330acl_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
int ret;
ret = lsm330acl_dvr_open((FAR void *)priv, 0);
return ret;
}
/****************************************************************************
* Name: lsm330gyro_open
****************************************************************************/
static int lsm330gyro_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
int ret;
ret = lsm330gyro_dvr_open((FAR void *)priv, 0);
return ret;
}
/****************************************************************************
* Name: lsm330acl_close
****************************************************************************/
static int lsm330acl_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
int ret;
ret = lsm330acl_dvr_close((FAR void *)priv, 0);
return ret;
}
/****************************************************************************
* Name: lsm330gyro_close
****************************************************************************/
static int lsm330gyro_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
int ret;
ret = lsm330gyro_dvr_close((FAR void *)priv, 0);
return ret;
}
/****************************************************************************
* Name: lsm330acl_read
****************************************************************************/
static ssize_t lsm330acl_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330acl_dvr_read(priv, buffer, buflen);
}
/****************************************************************************
* Name: lsm330gyro_read
****************************************************************************/
static ssize_t lsm330gyro_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330gyro_dvr_read(priv, buffer, buflen);
}
/****************************************************************************
* Name: lsm330acl_write
****************************************************************************/
static ssize_t lsm330acl_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330acl_dvr_write(priv, buffer, buflen);
}
/****************************************************************************
* Name: lsm330gyro_write
****************************************************************************/
static ssize_t lsm330gyro_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330gyro_dvr_write(priv, buffer, buflen);
}
/****************************************************************************
* Name: lsm330acl_seek
****************************************************************************/
static off_t lsm330acl_seek(FAR struct file *filep, off_t offset, int whence)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330acl_dvr_seek(priv, offset, whence);
}
/****************************************************************************
* Name: lsm330gyro_seek
****************************************************************************/
static off_t lsm330gyro_seek(FAR struct file *filep, off_t offset, int whence)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330gyro_dvr_seek(priv, offset, whence);
}
/****************************************************************************
* Name: lsm330_ioctl
****************************************************************************/
static int lsm330_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330_dvr_ioctl(priv, cmd, arg);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: lsm330_register
*
* Description:
* Register the LSM330 character device as 'devpath'
*
* Input Parameters:
* devpath_acl - The full path to the driver to register. E.g.,
* "/dev/acl0"
* devpath_gyro - The full path to the driver to register. E.g.,
* "/dev/gyr0"
* spi - An instance of the SPI interface to use to communicate
* with LSM330
* config_acl - configuration for the LSM330 accelerometer driver.
* For details see description above.
* config_gyro - configuration for the LSM330 gyroscope driver.
* For details see description above.
* caller_is_driver - 0 (false) Driver user is a user application using
* the fops interface.
* 1 (true) "Driver to Driver" interface will be used
* in addition to the fops interface.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int lsm330_register(FAR const char *devpath_acl,
FAR const char *devpath_gyro,
FAR struct spi_dev_s *spi,
FAR struct lsm330_config_s *config_acl,
FAR struct lsm330_config_s *config_gyro)
{
FAR struct lsm330_dev_s *priva;
FAR struct lsm330_dev_s *priv;
int ret;
/* Sanity check */
DEBUGASSERT(spi != NULL);
DEBUGASSERT(config_acl != NULL);
DEBUGASSERT(config_gyro != NULL);
config_acl->leaf_handle = NULL;
config_gyro->leaf_handle = NULL;
config_acl->sc_ops = NULL;
config_gyro->sc_ops = NULL;
/* Initialize the LSM330 accelerometer device structure. */
priv = (FAR struct lsm330_dev_s *)kmm_malloc(sizeof(struct lsm330_dev_s));
if (priv == NULL)
{
snerr("ERROR: Failed to allocate accelerometer instance\n");
return -ENOMEM;
}
priv->spi = spi;
priv->config = config_acl;
/* Initialize sensor and sensor data access semaphore */
nxsem_init(&priv->devicesem, 0, 1);
nxsem_init(&priv->datasem, 0, 1);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(spi, LSM330_SPI_MODE);
/* Register the character driver */
ret = register_driver(devpath_acl, &g_lsm330a_fops, 0666, priv);
if (ret < 0)
{
snerr("ERROR: Failed to register accelerometer driver: %d\n", ret);
nxsem_destroy(&priv->datasem);
kmm_free(priv);
return ret;
}
/* Since we support multiple LSM330 devices, we will need to add this new
* instance to a list of device instances so that it can be found by the
* interrupt handler based on the received IRQ number.
*/
priv->flink = g_lsm330a_list;
g_lsm330a_list = priv;
priva = priv;
config_acl->leaf_handle = (void *) priv;
/* Initialize the LSM330 gyroscope device structure. */
priv = (FAR struct lsm330_dev_s *)kmm_malloc(sizeof(struct lsm330_dev_s));
if (priv == NULL)
{
snerr("ERROR: Failed to allocate gyroscope instance\n");
ret = -ENOMEM;
goto err_exit;
}
priv->spi = spi;
priv->config = config_gyro;
/* Initialize sensor and sensor data access semaphore */
nxsem_init(&priv->devicesem, 0, 1);
nxsem_init(&priv->datasem, 0, 1);
/* Register the character driver */
ret = register_driver(devpath_gyro, &g_lsm330g_fops, 0666, priv);
if (ret < 0)
{
snerr("ERROR: Failed to register gyroscope driver: %d\n", ret);
nxsem_destroy(&priv->datasem);
kmm_free(priv);
goto err_exit;
}
/* Since we support multiple LSM330 devices, we will need to add this new
* instance to a list of device instances so that it can be found by the
* interrupt handler based on the received IRQ number.
*/
priv->flink = g_lsm330g_list;
g_lsm330g_list = priv;
config_gyro->leaf_handle = (void *) priv;
config_acl->sc_ops = &g_lsm330acl_dops;
config_gyro->sc_ops = &g_lsm330gyro_dops;
/* If this is part of a kernel controlled sensor cluster driver,
* then return a handle to the caller
*/
return OK;
err_exit:
/* Registration the of the gyroscope failed, so we need to destroy the
* accelerometer instance.
*/
nxsem_destroy(&priva->datasem);
kmm_free(priva);
return ret;
}
#endif /* CONFIG_SPI && CONFIG_SENSORS_LSM330SPI && CONFIG_SPI_EXCHANGE */