input子系统和misc设备驱动
/*
* bma250.c - Linux kernel modules for 3-Axis Accel sensor
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/input-polldev.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/gpio.h>
#include "bma250.h"
//ioctl命令构建
#define SENSOR_IOCTL_BASE 'S'
#define SENSOR_GET_MODEL_NAME _IOR(SENSOR_IOCTL_BASE, 0, char *)
#define SENSOR_GET_POWER_STATUS _IOR(SENSOR_IOCTL_BASE, 2, int)
#define SENSOR_SET_POWER_STATUS _IOR(SENSOR_IOCTL_BASE, 3, int)
#define SENSOR_GET_DELAY_TIME _IOR(SENSOR_IOCTL_BASE, 4, int)
#define SENSOR_SET_DELAY_TIME _IOR(SENSOR_IOCTL_BASE, 5, int)
#define SENSOR_SET_HIGH_TH _IOR(SENSOR_IOCTL_BASE, 6, int)
#define SENSOR_GET_HIGH_TH _IOR(SENSOR_IOCTL_BASE, 7, int)
#define SENSOR_SET_TAP_TH _IOR(SENSOR_IOCTL_BASE, 8, int)
#define SENSOR_GET_TAP_TH _IOR(SENSOR_IOCTL_BASE, 9, int)
#define SENSOR_SET_TAP_QUIET _IOR(SENSOR_IOCTL_BASE, 10, int)
#define SENSOR_GET_TAP_QUIET _IOR(SENSOR_IOCTL_BASE, 11, int)
#define SENSOR_SET_TAP_SHOCK _IOR(SENSOR_IOCTL_BASE, 12, int)
#define SENSOR_GET_TAP_SHOCK _IOR(SENSOR_IOCTL_BASE, 13, int)
#define SENSOR_GET_RAW_DATA _IOR(SENSOR_IOCTL_BASE, 14, short[3])
#define BMA250_POSITION_DEFAULT 2
#define BMA250_DELAY_DEFAULT 200
#define BMA250_TAP_TH_DEFAULT 0x18
#define BMA250_STATUS_ZYXDR 0x08
#define BMA250_BUF_SIZE 6
struct bma250_data bma250_dev;
#if 0
static int bma250_position_setting[8][3][3] = {
{{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, sizeof(int))) {
printk(KERN_ERR "SENSOR_GET_HIGH_TH copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_SET_TAP_QUIET:
if (copy_from_user(&tap_quiet, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_HIGH_TH copy_to_user failed.");
ret = -EFAULT;
}
if (pdata) {
ret = bma250_change_tap_quiet(pdata, tap_quiet);
if (!ret)
atomic_set(&pdata->tap_quiet, tap_quiet);
}
break;
case SENSOR_GET_TAP_SHOCK:
tap_th = atomic_read(&pdata->tap_shock);
if (copy_to_user(argp, &tap_shock, sizeof(int))) {
printk(KERN_ERR "SENSOR_GET_HIGH_TH copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_SET_TAP_SHOCK:
if (copy_from_user(&tap_shock, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_HIGH_TH copy_to_user failed.");
ret = -EFAULT;
}
if (pdata) {
ret = bma250_change_tap_shock(pdata, tap_shock);
if (!ret)
atomic_set(&pdata->tap_shock, tap_shock);
}
break;
case SENSOR_GET_RAW_DATA:
ret = bma250_read_data(pdata, &data);
if (!ret) {
bma250_data_convert(pdata, &data);
sdata[0] = data.x;
sdata[1] = data.y;
sdata[2] = data.z;
if (copy_to_user(argp, sdata, sizeof(sdata))) {
printk(KERN_ERR "SENSOR_GET_RAW_DATA copy_to_user failed.");
ret = -EFAULT;
}
}
break;
default:
ret = -1;
}
return ret;
}
static const struct file_operations bma250_fops = {
.owner = THIS_MODULE,
.open = bma250_open,
.release = bma250_release,
.unlocked_ioctl = bma250_ioctl,
};
static struct miscdevice bma250_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "bma_gsensor",
.fops = &bma250_fops,
};
/*************
*******用于测试,在misc下创建相关测试文件
**************/
static ssize_t bma250_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct bma250_data *pdata = &bma250_dev;
int enable = 0;
enable = atomic_read(&pdata->active);
return sprintf(buf, "%d\n", enable);
}
static ssize_t bma250_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct bma250_data *pdata = &bma250_dev;
int ret;
unsigned long enable;
if(kstrtoul(buf, 10, &enable) < 0) //将buf对于的字符串转换为10进制数字,存入enable变量中。
return -EINVAL;
//enable = (enable > 0) ? 1 : 0;
ret = bma250_change_mode(pdata, enable);
if (!ret) {
atomic_set(&pdata->active, enable);
//if (enable)
printk(KERN_INFO "power status =0x%x\n", (int)enable);
//else
// printk(KERN_INFO "mma enable setting standby\n");
}
return count;
}
static ssize_t bma250_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct bma250_data *pdata = &bma250_dev;
int delay = 0;
delay = atomic_read(&pdata->delay);
return sprintf(buf, "%d\n", delay);
}
static ssize_t bma250_poll_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct bma250_data *pdata = &bma250_dev;
int ret;
unsigned long delay;
if(kstrtoul(buf, 10, &delay) < 0)
return -EINVAL;
ret = bma250_set_delay(pdata, delay);
if (!ret)
atomic_set(&pdata->delay, delay);
return count;
}
static ssize_t bma250_position_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct bma250_data *pdata = &bma250_dev;
int position = 0;
position = atomic_read(&pdata->position);
return sprintf(buf, "%d\n", position);
}
static ssize_t bma250_position_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct bma250_data *pdata = &bma250_dev;
unsigned long position;
if(kstrtoul(buf, 10, &position) < 0)
return -EINVAL;
atomic_set(&pdata->position, position);
return count;
}
static ssize_t bma250_data_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct bma250_data *pdata = &bma250_dev;
int ret = 0;
struct bma250_data_axis data;
ret = bma250_read_data(pdata, &data);
if (!ret)
bma250_data_convert(pdata, &data);
return sprintf(buf, "%d,%d,%d\n", data.x, data.y, data.z);
}
static ssize_t bma250_range_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct bma250_data *pdata = &bma250_dev;
int range = 0;
range = atomic_read(&pdata->range);
return sprintf(buf, "%d\n", range);
}
static ssize_t bma250_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct bma250_data *pdata = &bma250_dev;
int ret;
unsigned long range;
if (kstrtoul(buf, 10, &range) < 0)
return -EINVAL;
if (range == atomic_read(&pdata->range))
return count;
if (atomic_read(&pdata->active))
printk(KERN_INFO "Pls set the sensor standby and then actived\n");
ret = bma250_change_range(pdata, range);
if (!ret)
atomic_set(&pdata->range, range);
return count;
}
//使用DEVICE_ATTR,可以在sys fs中添加“文件”,通过修改该文件内容,可以实现在运行过程中动态控制device的目的。
//DEVICE_ATTR对应的文件在/sys/devices/目录中对应的device下面
/*
DEVICE_ATTR(_name, _mode, _show, _store)
_name:名称,也就是将在sys fs中生成的文件名称。
_mode:上述文件的访问权限,与普通文件相同,UGO的格式。
_show:显示函数,cat该文件时,此函数被调用。
_store:写函数,echo内容到该文件时,此函数被调用。
*/
static DEVICE_ATTR(enable, 0666, bma250_enable_show, bma250_enable_store);
static DEVICE_ATTR(poll_delay, 0666, bma250_poll_delay_show, bma250_poll_delay_store);
static DEVICE_ATTR(position, 0666, bma250_position_show, bma250_position_store);
static DEVICE_ATTR(data, 0666, bma250_data_show, NULL);
static DEVICE_ATTR(range, 0666, bma250_range_show, bma250_range_store);
static struct attribute *bma250_attributes[] = {
&dev_attr_enable.attr,
&dev_attr_poll_delay.attr,
&dev_attr_position.attr,
&dev_attr_data.attr,
&dev_attr_range.attr,
NULL
};
static const struct attribute_group bma250_attr_group = {
.attrs = bma250_attributes,
};
/*************
*****中断发生时的事件报告
**************/
static irqreturn_t bma250_irq_handler(int irq, void *dev)
{
int ret;
u8 int_src;
struct bma250_data *pdata = (struct bma250_data *)dev;
struct bma250_data_axis data;
u8 status_data[BMA250_BUF_SIZE];
bma250_bus_write(pdata, BMA250_INT_RST_LATCH, 0x8f); //clear latch, and latch intr 手工清除中断标志位
bma250_bus_read_block(pdata, BMA250_INT_STATUS_0, 4, status_data);
// int_src = bma250_bus_read(pdata, BMA250_INT_STATUS_1);
/* data ready interrupt */
// if (int_src & 0x80) {
ret = bma250_read_data(pdata, &data);
if (!ret) {
bma250_data_convert(pdata, &data);
input_report_abs(pdata->idev, ABS_X, data.x);
input_report_abs(pdata->idev, ABS_Y, data.y);
input_report_abs(pdata->idev, ABS_Z, data.z);
input_sync(pdata->idev); //告诉input_core此次汇报已结束
}
// }
my_debug ("%s: x = 0x%x, y=0x%x, z=0x%x, intr_status0=0x%x, intr_status1=0x%x, intr_status2=0x%x,intr_status3=0x%x\n",
__FUNCTION__, data.x, data.y, data.z,status_data[0], status_data[1], status_data[2], status_data[3]);
return IRQ_HANDLED;
}
/***************
****iic设备驱动注册调用
****************/
int bma250_driver_init(struct bma250_data *pdata)
{
int result, chip_id;
// 读取IIC chip id
chip_id = bma250_bus_read(pdata, BMA250_BGW_CHIPID);
#if 1
printk("gsensor id = 0x%x\n", chip_id);
if (chip_id != BMA250_ID) {
printk(KERN_ERR "read sensor who am i (0x%x)error !\n",
chip_id);
result = -EINVAL;
goto err_out;
}
#endif
/* Initialize the BMA250 chip */
pdata->chip_id = chip_id;
atomic_set(&pdata->delay, BMA250_DELAY_DEFAULT);
atomic_set(&pdata->position, BMA250_POSITION_DEFAULT);
//注册misc设备
result = misc_register(&bma250_device);
if (result != 0) {
printk(KERN_ERR "register acc miscdevice error");
goto err_regsiter_misc;
}
//创建调试文件
result = sysfs_create_group(&bma250_device.this_device->kobj, &bma250_attr_group);
if (result) {
printk(KERN_ERR "create device file failed!\n");
result = -EINVAL;
goto err_create_sysfs;
}
//分配input设备
pdata->idev = input_allocate_device();
if (!pdata->idev) {
result = -ENOMEM;
printk(KERN_ERR "alloc bma250 input device failed!\n");
goto err_alloc_input_device;
}
pdata->idev->name = "bma250Accel";
pdata->idev->id.bustype = BUS_I2C;
pdata->idev->evbit[0] = BIT_MASK(EV_ABS); //用于记录支持的事件类型的位图
input_set_abs_params(pdata->idev, ABS_X, -0x7fff, 0x7fff, 0, 0);
input_set_abs_params(pdata->idev, ABS_Y, -0x7fff, 0x7fff, 0, 0);
input_set_abs_params(pdata->idev, ABS_Z, -0x7fff, 0x7fff, 0, 0);
//注册input设备
result = input_register_device(pdata->idev);
if (result) {
printk(KERN_ERR "register bma250 input device failed!\n");
goto err_register_input_device;
}
//中断处理
if (pdata->irq) {
//申请input的中断,只定义了中断底半部。
//IRQF_ONESHOT:内核会自动在中断上下文中屏蔽对应中断号;内核调度底半部执行后,重新使能该中断号。
result = request_threaded_irq( pdata->irq, NULL, bma250_irq_handler,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
pdata->idev->name, pdata);
if (result < 0) {
printk(KERN_ERR "failed to register bma250 irq %d!\n",
pdata->irq);
goto err_register_irq;
}
//使一个irq具有唤醒系统的功能
result = enable_irq_wake(pdata->irq);
if(result) {
printk(KERN_ERR "failed to set bma250 gpio irq %d wake up!\n",
pdata->irq);
goto err_register_irq;
}
}
//设置Register 0x2B ,即设置tap中断灵敏度
bma250_change_tap_th(pdata, BMA250_TAP_TH_DEFAULT);
bma250_device_init(pdata);
printk("bma250 device driver probe successfully\n");
return 0;
err_register_irq:
input_unregister_device(pdata->idev);
err_register_input_device:
input_free_device(pdata->idev);
err_alloc_input_device:
sysfs_remove_group(&bma250_device.this_device->kobj, &bma250_attr_group);
err_create_sysfs:
misc_deregister(&bma250_device);
err_regsiter_misc:
//kfree(pdata);
err_out:
return result;
}
EXPORT_SYMBOL_GPL(bma250_driver_init);
int bma250_driver_remove(struct bma250_data *pdata)
{
bma250_change_mode(pdata, SUSPEND);
if (pdata->irq) {
free_irq(pdata->irq, pdata);
disable_irq_wake(pdata->irq);
}
//注销input设备
input_unregister_device(pdata->idev);
//释放input设备
input_free_device(pdata->idev);
sysfs_remove_group(&bma250_device.this_device->kobj,
&bma250_attr_group);
//注销misc设备
misc_deregister(&bma250_device);
// if (pdata != NULL)
// kfree(pdata);
return 0;
}
EXPORT_SYMBOL_GPL(bma250_driver_remove);
/***********
******做电源管理时用到
*************/
#ifdef CONFIG_PM_SLEEP
int bma250_driver_suspend(struct bma250_data *pdata)
{
// if (SUSPEND != atomic_read(&pdata->active))
// bma250_change_mode(pdata, SUSPEND);
return 0;
}
EXPORT_SYMBOL_GPL(bma250_driver_suspend);
int bma250_driver_resume(struct bma250_data *pdata)
{
// if (SUSPEND != atomic_read(&pdata->active))
// bma250_change_mode(pdata, atomic_read(&pdata->active));
return 0;
}
EXPORT_SYMBOL_GPL(bma250_driver_resume);
#endif
/*
* bma250.h - Linux kernel modules for 3-Axis Accel sensor
*
*/
#ifndef _BMA250_H
#define _BMA250_H
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/input.h>
#define BMA250_ID 0xf9
/* register enum for bma250 registers */
enum { BMA250_BGW_CHIPID = 0,
BMA250_ACCD_X_LSB = 0x02,
BMA250_ACCD_X_MSB,
BMA250_ACCD_Y_LSB,
BMA250_ACCD_Y_MSB,
BMA250_ACCD_Z_LSB,
BMA250_ACCD_Z_MSB,
BMA250_ACCD_TEMP,
BMA250_INT_STATUS_0,
BMA250_INT_STATUS_1,
BMA250_INT_STATUS_2,
BMA250_INT_STATUS_3,
BMA250_FIFO_STATUS = 0x0E,
BMA250_PMU_RANGE,
BMA250_PMU_BW,
BMA250_PMU_LPW,
BMA250_PMU_LOW_NOISE,
BMA250_ACCD_HBW,
BMA250_BGW_SOFTRESET,
BMA250_INT_EN_0 = 0x16,
BMA250_INT_EN_1,
BMA250_INT_EN_2,
BMA250_INT_MAP_0,
BMA250_INT_MAP_1,
BMA250_INT_MAP_2,
BMA250_INT_SRC = 0x1E,
BMA250_INT_OUT_CTRL = 0x20,
BMA250_INT_RST_LATCH,
BMA250_INT_0,
BMA250_INT_1,
BMA250_INT_2,
BMA250_INT_3,
BMA250_INT_4,
BMA250_INT_5,
BMA250_INT_6,
BMA250_INT_7,
BMA250_INT_8,
BMA250_INT_9,
BMA250_REG_END,
};
enum { ACTIVED = 0x01,
LOW_POWER = 0x02,
SUSPEND = 0x04
};
enum { MODE_2G = 0x03,
MODE_4G = 0x05,
MODE_8G = 0x08,
MODE_16G = 0x0C
};
enum { TAP_QUIET_30MS,
TAP_QUIET_20MS
};
enum { TAP_SHOCK_50MS,
TAP_SHOCK_75MS
};
struct bma250_data_axis {
short x;
short y;
short z;
};
struct bma250_data {
void *bus_priv; //为i2c_client结构
u16 bus_type;
int irq;
s32 (*write)(struct bma250_data *pdata, u8 reg, u8 val);
s32 (*read)(struct bma250_data *pdata, u8 reg);
s32 (*read_block)(struct bma250_data *pdata, u8 reg, u8 len, u8 *val);
struct input_dev *idev;
atomic_t active;
atomic_t high_th;
atomic_t tap_th;
atomic_t tap_quiet;
atomic_t tap_shock;
atomic_t delay;
atomic_t position;
atomic_t range;
u8 chip_id;
};
extern struct bma250_data bma250_dev;
int bma250_driver_init(struct bma250_data *pdata);
int bma250_driver_remove(struct bma250_data *pdata);
int bma250_driver_suspend(struct bma250_data *pdata);
int bma250_driver_resume(struct bma250_data *pdata);
#endif /* */
makefile
bma250_gsensor-objs := bma250.o bma250_i2c.o
obj-m := bma250_gsensor.o
PWD := $(shell pwd)
KDIR := /home/vec/nfs/tbox-4g/kernel/dir
all:
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.order Module.symvers