linux 3.18 -- iic,input,misc,三軸加速度裝置驅動(三)
阿新 • • 發佈:2019-02-13
{{0, -1, 0}, {1, 0, 0}, {0, 0, 1} },
{{-1, 0, 0}, {0, -1, 0}, {0, 0, 1} },
{{0, 1, 0}, {-1, 0, 0}, {0, 0, 1} },
{{1, 0, 0}, {0, 1, 0}, {0, 0, 1} },
{{0, -1, 0}, {-1, 0, 0}, {0, 0, -1} },
{{-1, 0, 0}, {0, 1, 0}, {0, 0, -1} },
{{0, 1, 0}, {1, 0, 0}, {0, 0, -1} },
{{1, 0, 0}, {0, -1, 0}, {0, 0, -1} },
};
#endif
#define DEBUG _SWITCH 1
#ifdef DEBUG_SWITCH
#define my_debug(fmt,args...) printk(fmt, ##args)
#else
#define my_debug(fmt,args...) /*do nothing */
#endif
static int bma250_bus_write(struct bma250_data *pdata, u8 reg, u8 val)
{
if (pdata && pdata->write)
//間接呼叫bma250_i2c_write()
return pdata->write(pdata, reg, val);
return -EIO;
}
static int bma250_bus_read(struct bma250_data *pdata, u8 reg)
{
if (pdata && pdata->read)
//間接呼叫bma250_i2c_read()
return pdata->read(pdata, reg);
return -EIO;
}
static int bma250_bus_read_block(struct bma250_data *pdata, u8 reg, u8 len,
u8 *val)
{
if (pdata && pdata->read_block)
//間接呼叫bma250_i2c_read_block
return pdata->read_block(pdata, reg, len, val);
return -EIO;
}
static int bma250_data_convert(struct bma250_data *pdata, struct bma250_data_axis *axis_data)
{
/*short rawdata[3], data[3];
int i, j;
int position = atomic_read(&pdata->position);
if (position < 0 || position > 7)
position = 0;
rawdata[0] = axis_data->x;
rawdata[1] = axis_data->y;
rawdata[2] = axis_data->z;
for (i = 0; i < 3; i++) {
data[i] = 0;
for (j = 0; j < 3; j++)
data[i] +=
rawdata[j] *
bma250_position_setting[position][i][j];
}
axis_data->x = data[0];
axis_data->y = data[1];
axis_data->z = data[2];*/
return 0;
}
/*************
************bma250功能函式
**************/
static int bma250_device_init(struct bma250_data *pdata)
{
int result;
//Selection of the main power modes and the low power sleep period
result = bma250_bus_write(pdata, BMA250_PMU_LPW, 0); //set the normal mode
if (result < 0)
goto out;
//The register allows the selection of the accelerometer g-range.
result = bma250_bus_write(pdata, BMA250_PMU_RANGE, MODE_2G); //selection the MODE_2G
if (result < 0)
goto out;
if (pdata->irq) {
//Contains the interrupt reset bit and the interrupt mode selection.
result = bma250_bus_write(pdata, BMA250_INT_RST_LATCH, 0x8f); // clear any latched interrupts, and latch intr
if (result < 0)
goto out;
//Contains the behavioural configuration (electrical behaviour) of the interrupt pins.
#if 0
result = bma250_bus_write(pdata, BMA250_INT_OUT_CTRL, 0x01); //intr 1:push-pull,active high; intr 2:push-pull,active low
if (result < 0)
goto out;
#endif
/*配置為高有效,即有上升沿*/
result = bma250_bus_write(pdata, BMA250_INT_OUT_CTRL, 0x05); //intr 1:push-pull,active high; intr 2:push-pull,active high
if (result < 0)
goto out;
// result = bma250_bus_write(pdata, BMA250_INT_MAP_1, 0x1);
// result = bma250_bus_write(pdata, BMA250_INT_MAP_1, 0xff);
// if (result < 0)
// goto out;
//Controls which interrupt signals are mapped to the INT1 pin.
result = bma250_bus_write(pdata, BMA250_INT_MAP_0, 0x22); // map the single tap and hight g to int1
if (result < 0)
goto out;
/*對映中斷源到int2*/
result = bma250_bus_write(pdata, BMA250_INT_MAP_2, 0x22); // map the single tap and hight g to int2
if (result < 0)
goto out;
// result = bma250_bus_write(pdata, BMA250_INT_EN_1, 0x10);
// result = bma250_bus_write(pdata, BMA250_INT_EN_0, 0x30);
// if (result < 0)
// goto out;
// result = bma250_bus_write(pdata, BMA250_INT_EN_1, 0);
// if (result < 0)
// goto out;
// printk("BMA250 77\n");
//Controls which interrupt engines in group 0 are enabled
result = bma250_bus_write(pdata, BMA250_INT_EN_0, 0x20); //enable single tap interrupt
if (result < 0)
goto out;
//Controls which interrupt engines in group 1 are enabled
result = bma250_bus_write(pdata, BMA250_INT_EN_1, 0x07); //enable high-g interrupt:x-axis, y-axis, z-axis
if (result < 0)
goto out;
}
my_debug ("%s: irq = 0x%x\n", __FUNCTION__, pdata->irq);
atomic_set(&pdata->active, ACTIVED);
return 0;
out:
printk("BMA250 device init error\n");
return result;
}
//Selection of the main power modes and the low power sleep period 之修改睡眠時間
static int bma250_set_delay(struct bma250_data *pdata, int delay)
{
u8 val;
if (delay < 0)
return 0;
val = bma250_bus_read(pdata, BMA250_PMU_LPW);
/* set sensor */
// bma250_bus_write(pdata, BMA250_CTRL_REG1, (val & ~0xd0));
val &= ~0x1e;
if (delay < 1)
val |= 0x00 << 1;
else if (delay < 2)
val |= 0x06 << 1;
else if (delay < 4)
val |= 0x07 << 1;
else if (delay < 6)
val |= 0x08 << 1;
else if (delay < 10)
val |= 0x09 << 1;
else if (delay < 25)
val |= 0x0a << 1;
else if (delay < 50)
val |= 0x0b << 1;
else if (delay < 100)
val |= 0x0c << 1;
else if (delay < 500)
val |= 0x0d << 1;
else if (delay < 1000)
val |= 0x0e << 1;
else
val |= 0x0f << 1;
bma250_bus_write(pdata, BMA250_PMU_LPW, val);
atomic_set(&pdata->active, ACTIVED);
my_debug ("%s: val = 0x%x, delay=0x%x\n", __FUNCTION__, val, delay);
return 0;
}
//Selection of the main power modes and the low power sleep period 之修改工作模式
static int bma250_change_mode(struct bma250_data *pdata, int mode)
{
u8 val;
int ret;
val = bma250_bus_read(pdata, BMA250_PMU_LPW); //pm 5:7
val &= ~0xe0;
if (mode == ACTIVED) {
/*if ((mode & 0xd0) == 0)
val |= 0x20;*/
//val &= ~0xe0;
//return bma250_set_delay(pdata, atomic_read(&pdata->delay));
} else if (mode == SUSPEND)
val |= (SUSPEND << 5);
else if (mode == LOW_POWER) {
val |= (LOW_POWER << 5);
bma250_bus_write(pdata, BMA250_PMU_LPW, val);
bma250_set_delay(pdata, atomic_read(&pdata->delay));
return 0;
}
my_debug ("%s: val = 0x%x\n", __FUNCTION__, val);
ret = bma250_bus_write(pdata, BMA250_PMU_LPW, val);
return ret;
}
//Contains the threshold definition for the high-g interrupt 修改high_g加速度上限閾值
static int bma250_change_high_th(struct bma250_data *pdata, int high_th)
{
int ret;
//Contains the threshold definition for the high-g interrupt
ret = bma250_bus_write(pdata, BMA250_INT_4, high_th);
my_debug ("%s: val = 0x%x\n", __FUNCTION__, high_th);
return ret;
}
//Contains the timing definitions for the single tap and double tap interrupts 之tap quiet設定
static int bma250_change_tap_quiet(struct bma250_data *pdata, int tap_quiet)
{
int ret,val;
if(tap_quiet)
tap_quiet = 0x80;
val = bma250_bus_read(pdata, BMA250_INT_8);
val &= 0x7f;
val |= tap_quiet;
ret = bma250_bus_write(pdata, BMA250_INT_8, val);
my_debug ("%s: val = 0x%x\n", __FUNCTION__, val);
return ret;
}
//Contains the timing definitions for the single tap and double tap interrupts 之tap shock設定
static int bma250_change_tap_shock(struct bma250_data *pdata, int tap_shock)
{
int ret,val;
if(tap_shock)
tap_shock = 0x40;
val = bma250_bus_read(pdata, BMA250_INT_8);
val &= 0x7f;
val |= tap_shock;
ret = bma250_bus_write(pdata, BMA250_INT_8, val);
my_debug ("%s: val = 0x%x\n", __FUNCTION__, val);
return ret;
}
//defines the threshold definition for the single and double tap interrupts 之tap中斷閾值設定
static int bma250_change_tap_th(struct bma250_data *pdata, int tap_th)
{
int ret;
tap_th &= 0x1f;
ret = bma250_bus_write(pdata, BMA250_INT_9, tap_th);
my_debug ("%s: val = 0x%x\n", __FUNCTION__, tap_th);
return ret;
}
//The register allows the selection of the accelerometer g-range
static int bma250_change_range(struct bma250_data *pdata, int range)
{
int ret;
/*u8 val;
val = bma250_bus_read(pdata, BMA250_PMU_RANGE);
val &= ~0x0f;
val |= ((range&0x3) << 4);*/
range &= 0x0f;
ret = bma250_bus_write(pdata, BMA250_PMU_RANGE, range);
my_debug ("%s: val = 0x%x\n", __FUNCTION__, range);
return ret;
}
//讀取加速度值
static int bma250_read_data(struct bma250_data *pdata, struct bma250_data_axis *data)
{
u8 tmp_data[BMA250_BUF_SIZE];
int ret;
//一次讀6個位元組 x,y,z
ret = bma250_bus_read_block(pdata, BMA250_ACCD_X_LSB, BMA250_BUF_SIZE, tmp_data);
if (ret < BMA250_BUF_SIZE) {
printk(KERN_ERR "BMA250 read sensor block data error\n");
return -EIO;
}
//注意左移右移是按機器位寬進行
data->x = ((tmp_data[1]&0xff) << 2) | ((tmp_data[0]&0xc0)>>6);
data->y = ((tmp_data[3]&0xff) << 2) | ((tmp_data[2]&0xc0)>>6);
data->z = ((tmp_data[5]&0xff) << 2) | ((tmp_data[4]&0xc0)>>6);
// my_debug ("%s: x = 0x%x, y=0x%x, z=0x%x, data[0]=0x%x,data[1]=0x%x,data[2]=0x%x,data[3]=0x%x,data[4]=0x%x,data[5]=0x%x\n", __FUNCTION__,
// data->x, data->y, data->z, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3], tmp_data[4], tmp_data[5]);
return 0;
}
/********************
*********misc裝置
*********************/
static int bma250_open(struct inode *inode, struct file *file)
{
//指向bma250裝置私有資料。還可以通過動態申請的方式
file->private_data = &bma250_dev;
return nonseekable_open(inode, file);
}
static int bma250_release(struct inode *inode, struct file *file)
{
/*note: releasing the wdt in NOWAYOUT-mode does not stop it */
return 0;
}
static long bma250_ioctl(struct file *file, unsigned int reg, unsigned long arg)
{
struct bma250_data *pdata = file->private_data;
void __user *argp = (void __user *)arg; //通過arg引數,返回給使用者空間資料
long ret = 0;
short sdata[3];
int power_status, high_th, tap_th, tap_quiet, tap_shock;
int delay;
struct bma250_data_axis data;
if (!pdata) {
printk(KERN_ERR "BMA250 struct datt point is NULL.");
return -EFAULT;
}
switch (reg) {
case SENSOR_GET_MODEL_NAME:
if (copy_to_user(argp, "bma250", strlen("bma250") + 1)) {
printk(KERN_ERR "SENSOR_GET_MODEL_NAME copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_GET_POWER_STATUS:
power_status = atomic_read(&pdata->active);
if (copy_to_user(argp, &power_status, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_POWER_STATUS copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_SET_POWER_STATUS:
if (copy_from_user(&power_status, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_POWER_STATUS copy_to_user failed.");
ret = -EFAULT;
}
if (pdata) {
ret = bma250_change_mode(pdata, power_status);
if (!ret)
atomic_set(&pdata->active, power_status);
}
break;
case SENSOR_GET_DELAY_TIME:
delay = atomic_read(&pdata->delay);
if (copy_to_user(argp, &delay, sizeof(delay))) {
printk(KERN_ERR "SENSOR_GET_DELAY_TIME copy_to_user failed.");
return -EFAULT;
}
break;
case SENSOR_SET_DELAY_TIME:
if (copy_from_user(&delay, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_GET_DELAY_TIME copy_to_user failed.");
ret = -EFAULT;
}
if (pdata && delay > 0 && delay <= 500) {
ret = bma250_set_delay(pdata, delay);
if (!ret)
atomic_set(&pdata->delay, delay);
}
break;
case SENSOR_GET_HIGH_TH:
high_th = atomic_read(&pdata->high_th);
if (copy_to_user(argp, &high_th, sizeof(int))) {
printk(KERN_ERR "SENSOR_GET_HIGH_TH copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_SET_HIGH_TH:
if (copy_from_user(&high_th, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_HIGH_TH copy_to_user failed.");
ret = -EFAULT;
}
if (pdata) {
ret = bma250_change_high_th(pdata, high_th);
if (!ret)
atomic_set(&pdata->high_th, high_th);
}
break;
case SENSOR_GET_TAP_TH:
tap_th = atomic_read(&pdata->tap_th);
if (copy_to_user(argp, &tap_th, sizeof(int))) {
printk(KERN_ERR "SENSOR_GET_HIGH_TH copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_SET_TAP_TH:
if (copy_from_user(&tap_th, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_HIGH_TH copy_to_user failed.");
ret = -EFAULT;
}
if (pdata) {
ret = bma250_change_tap_th(pdata, tap_th);
if (!ret)
atomic_set(&pdata->tap_th, tap_th);
}
break;
case SENSOR_GET_TAP_QUIET:
tap_th = atomic_read(&pdata->tap_quiet);
if (copy_to_user(argp, &tap_quiet