linux 3.18 -- iic,input,misc,三轴加速度设备驱动(三)

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
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值