mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
Permissions (Part 2) Description: In kernel builds, any unprivileged process running on the NuttX device can open /dev/efuse and attempt to read/write fuse content. Reading the fuses may provide valuable information to an attacker controlling the user process. The write operation, in extreme cases where the fuse blocks are not locked, may brick the device. DISCLAIMER: I tried to be strict with the settings, better to relax them later if it's needed. This is part of https://github.com/apache/nuttx/issues/19410 See https://github.com/apache/nuttx/issues/19410 Compiles ok. Signed-off-by: Catalin Visinescu <catalin_visinescu@yahoo.com>
728 lines
20 KiB
C
728 lines
20 KiB
C
/****************************************************************************
|
|
* drivers/sensors/ak09912.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* WARNING for developers:
|
|
*
|
|
* This driver uses the legacy style of writing sensor drivers for NuttX. The
|
|
* project has since decided to adopt a new sensor framework in order to
|
|
* have a consistent API and feature-set.
|
|
*
|
|
* Sensors which use the uORB framework are typically suffixed "_uorb". You
|
|
* can also visit the documentation about the new sensor framework to learn
|
|
* more.
|
|
*/
|
|
|
|
#warning "This is a deprecated legacy sensor driver."
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fixedmath.h>
|
|
#include <errno.h>
|
|
#include <nuttx/debug.h>
|
|
#include <arch/types.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/i2c/i2c_master.h>
|
|
#include <nuttx/semaphore.h>
|
|
#include <nuttx/sensors/ak09912.h>
|
|
#include <nuttx/wdog.h>
|
|
#include <nuttx/irq.h>
|
|
|
|
#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_AK09912)
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define AK09911_DEVID 0x05
|
|
#define AK09912_DEVID 0x04
|
|
#define AK09912_ADDR 0x0C
|
|
#define AK09912_FREQ 400000
|
|
|
|
/* REGISTER: WIA
|
|
* Who I am.
|
|
*/
|
|
|
|
#define AK09912_WIA1 0x00
|
|
#define AK09912_WIA2 0x01
|
|
|
|
/* REGISTER: CNTL2
|
|
* Power mode
|
|
*/
|
|
|
|
#define POWER_MODE_ADDR 0x31
|
|
|
|
/* REGISTER: ASAX
|
|
* Sensitivity values
|
|
*/
|
|
|
|
#define AK09912_ASAX 0x60
|
|
|
|
/* REGISTER: CNTL1
|
|
* Enable or disable temparator measure or enable or disable Noise
|
|
* suppression filter.
|
|
*/
|
|
|
|
#define AK09912_CTRL1 0x30
|
|
|
|
/* REGISTER: HXL
|
|
* The start address of data registers.
|
|
*/
|
|
|
|
#define AK09912_HXL 0x11
|
|
|
|
/* Polling timeout
|
|
* The unit is 10 millisecond.
|
|
*/
|
|
|
|
#define AK09912_POLLING_TIMEOUT (1) /* 10 ms */
|
|
|
|
/* The parameter for compensating. */
|
|
|
|
#define AK09912_SENSITIVITY (128)
|
|
#define AK09912_SENSITIVITY_DIV (256)
|
|
|
|
/* Noise Suppression Filter */
|
|
|
|
#define AK09912_NSF_NONE 0b00
|
|
#define AK09912_NSF_LOW 0b01
|
|
#define AK09912_NSF_MIDDLE 0b10
|
|
#define AK09912_NSF_HIGH 0b11
|
|
|
|
/* Power mode */
|
|
|
|
#define AKM_POWER_DOWN_MODE 0b0000
|
|
#define AKM_SINGL_MEAS_MODE 0b00001
|
|
#define AKM_CONT_MEAS_1 0b00010
|
|
#define AKM_CONT_MEAS_2 0b00100
|
|
#define AKM_CONT_MEAS_3 0b00110
|
|
#define AKM_CONT_MEAS_4 0b01000
|
|
#define AKM_EXT_TRIG_MODE 0b01010
|
|
#define AKM_SELF_TEST_MODE 0b10000
|
|
#define AKM_FUSE_ROM_MODE 0b11111
|
|
|
|
/* REGISTER: ST1
|
|
* DRDY: Data ready bit. 0: not ready, 1: ready
|
|
* DOR: Data overrun. 0: Not overrun, 1: overrun
|
|
*/
|
|
|
|
#define AK09912_ST1 0x10
|
|
|
|
#define SET_BITSLICE(s, v, offset, mask) \
|
|
do { \
|
|
s &= mask; \
|
|
s |= (v << offset) & mask;} while(0)
|
|
|
|
#define MERGE_BYTE(low, high) \
|
|
((low & 0xff) | ((high << 8) & ~0xff))
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* Structure for compensating data. */
|
|
|
|
struct sensi_data_s
|
|
{
|
|
uint8_t x;
|
|
uint8_t y;
|
|
uint8_t z;
|
|
};
|
|
|
|
/* Structure for ak09912 device */
|
|
|
|
struct ak09912_dev_s
|
|
{
|
|
FAR struct i2c_master_s *i2c; /* I2C interface */
|
|
uint8_t addr; /* I2C address */
|
|
int freq; /* Frequency <= 3.4MHz */
|
|
int compensated; /* 0: uncompensated, 1:compensated */
|
|
struct sensi_data_s asa_data; /* sensitivity data */
|
|
uint8_t mode; /* power mode */
|
|
uint8_t nsf; /* noise suppression filter setting */
|
|
struct wdog_s wd;
|
|
sem_t wait;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* Character driver methods */
|
|
|
|
static int ak09912_open(FAR struct file *filep);
|
|
static int ak09912_close(FAR struct file *filep);
|
|
static ssize_t ak09912_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static ssize_t ak09912_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen);
|
|
static int ak09912_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct file_operations g_ak09912fops =
|
|
{
|
|
ak09912_open, /* open */
|
|
ak09912_close, /* close */
|
|
ak09912_read, /* read */
|
|
ak09912_write, /* write */
|
|
NULL, /* seek */
|
|
ak09912_ioctl, /* ioctl */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_getreg8
|
|
*
|
|
* Description:
|
|
* Read from an 8-bit ak09912 register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint8_t ak09912_getreg8(FAR struct ak09912_dev_s *priv,
|
|
uint8_t regaddr)
|
|
{
|
|
struct i2c_msg_s msg[2];
|
|
uint8_t regval = 0;
|
|
int ret;
|
|
|
|
msg[0].frequency = priv->freq;
|
|
msg[0].addr = priv->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].buffer = ®addr;
|
|
msg[0].length = 1;
|
|
|
|
msg[1].frequency = priv->freq;
|
|
msg[1].addr = priv->addr;
|
|
msg[1].flags = I2C_M_READ;
|
|
msg[1].buffer = ®val;
|
|
msg[1].length = 1;
|
|
|
|
ret = I2C_TRANSFER(priv->i2c, msg, 2);
|
|
if (ret < 0)
|
|
{
|
|
snerr("I2C_TRANSFER failed: %d\n", ret);
|
|
return 0;
|
|
}
|
|
|
|
return regval;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_putreg8
|
|
*
|
|
* Description:
|
|
* Write to an 8-bit ak09912 register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ak09912_putreg8(FAR struct ak09912_dev_s *priv,
|
|
uint8_t regaddr, uint8_t regval)
|
|
{
|
|
struct i2c_msg_s msg[2];
|
|
int ret;
|
|
uint8_t txbuffer[2];
|
|
|
|
txbuffer[0] = regaddr;
|
|
txbuffer[1] = regval;
|
|
|
|
msg[0].frequency = priv->freq;
|
|
msg[0].addr = priv->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].buffer = txbuffer;
|
|
msg[0].length = 2;
|
|
|
|
ret = I2C_TRANSFER(priv->i2c, msg, 1);
|
|
if (ret < 0)
|
|
{
|
|
snerr("I2C_TRANSFER failed: %d\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_getreg
|
|
*
|
|
* Description:
|
|
* Read cnt bytes from a ak09912 register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int32_t ak09912_getreg(FAR struct ak09912_dev_s *priv,
|
|
uint8_t regaddr, FAR uint8_t *buffer,
|
|
uint32_t cnt)
|
|
{
|
|
struct i2c_msg_s msg[2];
|
|
int ret;
|
|
|
|
msg[0].frequency = priv->freq;
|
|
msg[0].addr = priv->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].buffer = ®addr;
|
|
msg[0].length = 1;
|
|
|
|
msg[1].frequency = priv->freq;
|
|
msg[1].addr = priv->addr;
|
|
msg[1].flags = I2C_M_READ;
|
|
msg[1].buffer = buffer;
|
|
msg[1].length = cnt;
|
|
|
|
ret = I2C_TRANSFER(priv->i2c, msg, 2);
|
|
if (ret < 0)
|
|
{
|
|
snerr("I2C_TRANSFER failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_delay_msec
|
|
*
|
|
* Description:
|
|
* System level delay
|
|
*
|
|
****************************************************************************/
|
|
|
|
void ak09912_delay_msek(uint16_t msek)
|
|
{
|
|
/* This is used to short delay without schedule. */
|
|
|
|
up_udelay(msek * 1000);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_set_power_mode
|
|
*
|
|
* Description:
|
|
* set POWER MODE for ak09912
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ak09912_set_power_mode(FAR struct ak09912_dev_s *priv,
|
|
uint32_t mode)
|
|
{
|
|
int ret = 0;
|
|
ret = ak09912_putreg8(priv, POWER_MODE_ADDR, (uint8_t)mode);
|
|
ak09912_delay_msek(1);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_read_sensitivity_data
|
|
*
|
|
* Description:
|
|
* read sensitivity date in fuse mode.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ak09912_read_sensitivity_data(FAR struct ak09912_dev_s *priv,
|
|
FAR struct sensi_data_s *asa_data)
|
|
{
|
|
int ret = 0;
|
|
uint8_t buffer[3];
|
|
|
|
ret = ak09912_getreg(priv, AK09912_ASAX, buffer, sizeof(buffer));
|
|
if (ret == 0)
|
|
{
|
|
asa_data->x = buffer[0];
|
|
asa_data->y = buffer[1];
|
|
asa_data->z = buffer[2];
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_set_noise_suppr_flt
|
|
*
|
|
* Description:
|
|
* set noise suppression filter for ak09912
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ak09912_set_noise_suppr_flt(FAR struct ak09912_dev_s *priv,
|
|
uint32_t nsf)
|
|
{
|
|
int ret = 0;
|
|
uint8_t ctrl1 = 0;
|
|
|
|
ctrl1 = ak09912_getreg8(priv, AK09912_CTRL1);
|
|
SET_BITSLICE(ctrl1, nsf, 5, 0x60);
|
|
ret = ak09912_putreg8(priv, AK09912_CTRL1, ctrl1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_wd_timeout
|
|
*
|
|
* Description:
|
|
* a timer to check if data is ready.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void ak09912_wd_timeout(wdparm_t arg)
|
|
{
|
|
struct ak09912_dev_s *priv = (struct ak09912_dev_s *)arg;
|
|
irqstate_t flags = enter_critical_section();
|
|
nxsem_post(&priv->wait);
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_read_mag_uncomp_data
|
|
*
|
|
* Description:
|
|
* read mag uncompensating data.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ak09912_read_mag_uncomp_data(FAR struct ak09912_dev_s *priv,
|
|
FAR struct mag_data_s *mag_data)
|
|
{
|
|
int ret = 0;
|
|
uint8_t state = 0;
|
|
uint8_t buffer[8]; /* TMPS and ST2 is read, but the value is omitted. */
|
|
|
|
wd_start(&priv->wd, AK09912_POLLING_TIMEOUT,
|
|
ak09912_wd_timeout, (wdparm_t)priv);
|
|
state = ak09912_getreg8(priv, AK09912_ST1);
|
|
while (! (state & 0x1))
|
|
{
|
|
nxsem_wait(&priv->wait);
|
|
}
|
|
|
|
wd_cancel(&priv->wd);
|
|
ret = ak09912_getreg(priv, AK09912_HXL, buffer, sizeof(buffer));
|
|
|
|
mag_data->x = MERGE_BYTE(buffer[0], buffer[1]);
|
|
mag_data->y = MERGE_BYTE(buffer[2], buffer[3]);
|
|
mag_data->z = MERGE_BYTE(buffer[4], buffer[5]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_read_mag_data
|
|
*
|
|
* Description:
|
|
* read mag data with compensation
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ak09912_read_mag_data(FAR struct ak09912_dev_s *priv,
|
|
FAR struct mag_data_s *mag_data)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = ak09912_read_mag_uncomp_data(priv, mag_data);
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to read mag data from device.\n");
|
|
return ret;
|
|
}
|
|
|
|
mag_data->x = (int16_t)(mag_data->x *
|
|
((int32_t)priv->asa_data.x + AK09912_SENSITIVITY) /
|
|
AK09912_SENSITIVITY_DIV);
|
|
mag_data->y = (int16_t)(mag_data->y *
|
|
((int32_t)priv->asa_data.y + AK09912_SENSITIVITY) /
|
|
AK09912_SENSITIVITY_DIV);
|
|
mag_data->z = (int16_t)(mag_data->z *
|
|
((int32_t)priv->asa_data.z + AK09912_SENSITIVITY) /
|
|
AK09912_SENSITIVITY_DIV);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_checkid
|
|
*
|
|
* Description:
|
|
* Read and verify the AK09912 chip ID
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ak09912_checkid(FAR struct ak09912_dev_s *priv)
|
|
{
|
|
uint16_t devid = 0;
|
|
|
|
/* Read device ID */
|
|
|
|
devid = ak09912_getreg8(priv, AK09912_WIA1);
|
|
devid += ak09912_getreg8(priv, AK09912_WIA2) << 8;
|
|
sninfo("devid: 0x%02x\n", devid);
|
|
|
|
if (devid != AK09911_DEVID && devid != AK09912_DEVID)
|
|
{
|
|
/* ID is not Correct */
|
|
|
|
snerr("Wrong Device ID! %02x\n", devid);
|
|
return -ENODEV;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_updatecaldata
|
|
*
|
|
* Description:
|
|
* Update Calibration Coefficient Data
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ak09912_initialize(FAR struct ak09912_dev_s *priv)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret += ak09912_set_power_mode(priv, AKM_FUSE_ROM_MODE);
|
|
ret += ak09912_read_sensitivity_data(priv, &priv->asa_data);
|
|
ret += ak09912_set_power_mode(priv, AKM_POWER_DOWN_MODE);
|
|
ret += ak09912_set_noise_suppr_flt(priv, priv->nsf);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_open
|
|
*
|
|
* Description:
|
|
* This function is called whenever the AK09912 device is opened.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ak09912_open(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct ak09912_dev_s *priv = inode->i_private;
|
|
int ret = 0;
|
|
|
|
ret = ak09912_set_power_mode(priv, priv->mode);
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to set power mode to %d.\n", priv->mode);
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_close
|
|
*
|
|
* Description:
|
|
* This routine is called when the AK09912 device is closed.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ak09912_close(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct ak09912_dev_s *priv = inode->i_private;
|
|
int ret = 0;
|
|
|
|
ret = ak09912_set_power_mode(priv, AKM_POWER_DOWN_MODE);
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to set power mode to %d.\n", AKM_POWER_DOWN_MODE);
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_read
|
|
****************************************************************************/
|
|
|
|
static ssize_t ak09912_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct ak09912_dev_s *priv = inode->i_private;
|
|
int32_t ret = 0;
|
|
FAR struct mag_data_s *mag_data = (FAR struct mag_data_s *)buffer;
|
|
|
|
if (! buffer)
|
|
{
|
|
snerr("Buffer is null\n");
|
|
return -1;
|
|
}
|
|
|
|
if (buflen != sizeof(struct mag_data_s))
|
|
{
|
|
snerr("You can't read something other than 32 bits (4 bytes)\n");
|
|
return -1;
|
|
}
|
|
|
|
if (priv->compensated)
|
|
{
|
|
irqstate_t flags = enter_critical_section();
|
|
ret = ak09912_read_mag_data(priv, mag_data);
|
|
leave_critical_section(flags);
|
|
}
|
|
else
|
|
{
|
|
irqstate_t flags = enter_critical_section();
|
|
ret = ak09912_read_mag_uncomp_data(priv, mag_data);
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to read data from ak09912.\n");
|
|
return ret;
|
|
}
|
|
|
|
return sizeof(struct mag_data_s);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_write
|
|
****************************************************************************/
|
|
|
|
static ssize_t ak09912_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_write
|
|
****************************************************************************/
|
|
|
|
static int ak09912_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct ak09912_dev_s *priv = inode->i_private;
|
|
int ret = OK;
|
|
|
|
switch (cmd)
|
|
{
|
|
case SNIOC_ENABLE_COMPENSATED:
|
|
priv->compensated = (int)arg;
|
|
break;
|
|
default:
|
|
snerr("Unrecognized cmd: %d\n", cmd);
|
|
ret = - ENOTTY;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ak09912_register
|
|
*
|
|
* Description:
|
|
* Register the AK09912 character device as 'devpath'
|
|
*
|
|
* Input Parameters:
|
|
* devpath - The full path to the driver to register. E.g., "/dev/compass0"
|
|
* i2c - An instance of the I2C interface to use to communicate with
|
|
* AK09912
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) on success; a negated errno value on failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ak09912_register(FAR const char *devpath, FAR struct i2c_master_s *i2c)
|
|
{
|
|
FAR struct ak09912_dev_s *priv;
|
|
char path[16];
|
|
int ret;
|
|
|
|
/* Initialize the AK09912 device structure */
|
|
|
|
priv = kmm_zalloc(sizeof(struct ak09912_dev_s));
|
|
if (!priv)
|
|
{
|
|
snerr("Failed to allocate instance\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
priv->i2c = i2c;
|
|
priv->addr = AK09912_ADDR;
|
|
priv->freq = AK09912_FREQ;
|
|
priv->compensated = ENABLE_COMPENSATED;
|
|
nxsem_init(&priv->wait, 0, 0);
|
|
|
|
/* set default noise suppression filter. */
|
|
|
|
priv->nsf = AK09912_NSF_LOW;
|
|
|
|
/* set default power mode */
|
|
|
|
priv->mode = AKM_CONT_MEAS_4;
|
|
|
|
/* Check Device ID */
|
|
|
|
ret = ak09912_checkid(priv);
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to register driver: %d\n", ret);
|
|
kmm_free(priv);
|
|
return ret;
|
|
}
|
|
|
|
ret = ak09912_initialize(priv);
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to initialize physical device ak09912:%d\n", ret);
|
|
kmm_free(priv);
|
|
return ret;
|
|
}
|
|
|
|
/* Register the character driver */
|
|
|
|
snprintf(path, sizeof(path), "%s%d", devpath, 0);
|
|
ret = register_driver(path, &g_ak09912fops, 0640, priv);
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to register driver: %d\n", ret);
|
|
kmm_free(priv);
|
|
}
|
|
|
|
sninfo("AK09912 driver loaded successfully!\n");
|
|
return ret;
|
|
}
|
|
|
|
#endif /* CONFIG_I2C && CONFIG_SENSORS_AK09912 */
|