一,regmap

  对于spi和i2c,读写寄存器的框架不同,但设备本质一样,因此就有了regmap模型来对其进行简化,提供统一的接口函数来访问寄存器,而且使用很方便,使用时候,调用regmap的读写寄存器函数即可。
在这里插入图片描述
整体框架:

    struct regmap *regmap;
    struct regmap_config regmap_config;
	 /*对于spi的设备,之前的读写还需要对t->tx_buf,t->rx_buf等进行操作,再t放到message
	 中,最后再利用spi_sync发送,比较麻烦*/
	static  unsigned  char icm20608c_read_one_reg(struct icm20608c_dev *dev,u8 reg)
	{
	    u8 ret;
	    unsigned int data;
	    ret = regmap_read(dev->regmap,reg,&data);//调用regmap的读函数
	    return (u8)data;
	}
	
	void icm20608c_write_one_reg66(struct icm20608c_dev *dev,u8 reg,unsigned char data)
	{
	    regmap_write(dev->regmap,reg,data);//调用regmap的写函数
	}
	
	static int icm20608c_probe(struct spi_device *spi)
	{
	    int ret = 0;
	    icm20608c.regmap_config.reg_bits = 8;//寄存器大小为8bit
	    icm20608c.regmap_config.val_bits = 8;//寄存器数据值8bit
	    //icm20608c.regmap_config.read_flag_mask = 0x80;//读掩码
	    icm20608c.regmap = regmap_init_spi(spi,&icm20608c.regmap_config);//关键就是这个                    函	数,将regmap_config和spi设备关联起来,创建出regmap
	    if(IS_ERR(icm20608c.regmap))
	    {
	        return PTR_ERR(icm20608c.regmap);
	    }
    ........
	}
	 
	static int icm20608c_remove(struct spi_device *spi)
	{
			.........
	    regmap_exit(icm20608c.regmap)return 0;
	}  

二,IIO子系统

  Linux驱动喜欢把程序分层,并且一类的放到一起,IIO子系统就是针对ADC传感器,将比如加速度计,光传感器,气压计,磁力计等等设备,用一样的驱动模型。
  结构体iio_dev来表示具体的IIO设备,整体的驱动框架,还是要看设备,比如spi设备,创建的就是spi_driver。主要包含三个内容:①在probe函数中,申请iio_dev内存,进行初始化,注册iio_dev。②iio_chan_spec对通道的建立,比如icm20608设备,有加速度xyz轴,陀螺仪xyz轴和温度,一共七个数据,因此要建立七个通道,在文件中显示,包含:加速度+x或y或z轴+什么数据,比如原始数据,量程等。③iio_info操作函数中,read_raw和write_raw函数具体的实现,具体怎么样实现,还是依靠regmap_read和regmap_write函数,对寄存器进行操作。

2.1初始化相关工作

  注意,现在不建立全局变量了,之前会这样写:struct icm20608c_dev icm20608,从而在整个驱动中,直接调用icm20608。比如在remove函数中,想进行注销工作,先indio_dev = spi_get_drvdata(spi);获取indio_dev,再得到dev = iio_priv(indio_dev);,即原来没改过的icm20608地址。
  初始化工作中,devm_iio_device_alloc(&spi->dev, sizeof(*dev)) 做了两件事①分配一个struct iio_dev结构的内存,②额外分配sizeof(*dev)大小的内存空间(这里是struct icm20608c_dev的大小。
在这里插入图片描述
  iio_priv(indio_dev)是一个宏,它返回的是紧跟在iio_dev结构体后面的私有数据区域的指针,dev = iio_priv(indio_dev)获取的是紧跟在iio_dev后面的为驱动私有数据预留的内存区域的指针。

struct icm20608c_dev {
    struct spi_device *spi;
    struct regmap *regmap;
    struct regmap_config regmap_config;
    struct mutex lock;
};
/*完成设备号注册 节点注册 spi设备初始化*/
static int icm20608c_probe(struct spi_device *spi)
{
    int ret = 0;
    struct icm20608c_dev *dev;
    struct iio_dev *indio_dev;

    /*1.iio_dev申请内存*/
    indio_dev = devm_iio_device_alloc(&spi->dev,sizeof(*dev));
    if(!indio_dev)
    {
        return ret;
    }
    /*2.获取icm20608c_dev结构体地址*/
    dev = iio_priv(indio_dev);
    dev->spi = spi;
    spi_set_drvdata(spi,indio_dev);
    mutex_init(&dev->lock);
    /*3.初始化iio_dev成员变量*/
    indio_dev->dev.parent = &spi->dev;
    indio_dev->info = &icm20608c_info;
    indio_dev->name = ICMC20608C_NAME;
    indio_dev->modes = INDIO_DIRECT_MODE;
    indio_dev->channels = icm20608_channels;
    indio_dev->num_channels = ARRAY_SIZE(icm20608_channels);
    /*4.注册iio_dev*/
    ret = iio_device_register(indio_dev);
    if(ret<0)
    {
        return ret;
    }
    /*5.初始化regmap_config并且注册*/
    dev->regmap_config.reg_bits = 8;//寄存器大小为8bit
    dev->regmap_config.val_bits = 8;//寄存器数据值8bit
    //dev.regmap_config.read_flag_mask = 0x80;//读掩码
    dev->regmap = regmap_init_spi(spi,&dev->regmap_config);
    if(IS_ERR(dev->regmap))
    {
        goto regmap_error;
        return PTR_ERR(dev->regmap);
    }
    /*6.初始化SPI设备*/
    spi->mode = SPI_MODE_0;//时钟模式
    spi_setup(spi);
    /*7.寄存器初始化内容*/
    icm20608c_reg_init(dev);
    printk("probe success \r\n");
    return 0;
regmap_error:
    iio_device_unregister(indio_dev);    
    return ret;
}
static int icm20608c_remove(struct spi_device *spi)
{
    /*这段代码,为了获取icm20608c_dev的地址和iio_dev地址,以便进行解除*/
    struct icm20608c_dev *dev;
    struct iio_dev *indio_dev;
    indio_dev = spi_get_drvdata(spi);
    dev = iio_priv(indio_dev);
    /*删除regmap,注销iio_dev*/
    regmap_exit(dev->regmap);
    iio_device_unregister(indio_dev);    
    printk("remove success \r\n");
    return 0;
}

2.2 通道

  这个 ICM20608_CHAN(_type, _channel2, _index)宏,就相当于给带入里面的数进行赋值,创建出一个通道结构体,_type是表示各种数据,比如加速度IIO_ACCEL,当modified=1,channel2 为通道修饰符,表示对这个_type还有更具体的描述。  info_mask_shared_by_type 为相同type类型通道共有的(但是名字要一样,比如都是加速度类型,in_accel_sacle),info_mask_separate 为这个通道私有的(比如in_accel_x_raw),具体在文件中的顺序,依靠scan_index,为0时候,在最前面。
在这里插入图片描述
在这里插入图片描述
在文件中显示顺序:
在这里插入图片描述

#define ICM20608_CHAN(_type, _channel2, _index)                    \
	{                                                             \
		.type = _type,                                        \
		.modified = 1,                                        \
		.channel2 = _channel2,                                \
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	      \
				      BIT(IIO_CHAN_INFO_CALIBBIAS),   \
		.scan_index = _index,                                 \
		.scan_type = {                                        \
				.sign = 's',                          \
				.realbits = 16,                       \
				.storagebits = 16,                    \
				.shift = 0,                           \
				.endianness = IIO_BE,                 \
			     },                                       \
	}
enum inv_icm20608_scan {
	INV_ICM20608_SCAN_ACCL_X,
	INV_ICM20608_SCAN_ACCL_Y,
	INV_ICM20608_SCAN_ACCL_Z,
	INV_ICM20608_SCAN_TEMP,
	INV_ICM20608_SCAN_GYRO_X,
	INV_ICM20608_SCAN_GYRO_Y,
	INV_ICM20608_SCAN_GYRO_Z,
	INV_ICM20608_SCAN_TIMESTAMP,
};
static const struct iio_chan_spec icm20608_channels[] = {
    {
        .type = IIO_TEMP,
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
                            |BIT(IIO_CHAN_INFO_OFFSET)
                            |BIT(IIO_CHAN_INFO_SCALE),
        .scan_index = INV_ICM20608_SCAN_TEMP,
        .scan_type = {
                .sign = 's',
                .realbits = 16,
                .storagebits = 16,
                .shift = 0,
                .endianness = IIO_BE,
            },
    },
    ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_X,INV_ICM20608_SCAN_GYRO_X),
    ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_Y,INV_ICM20608_SCAN_GYRO_Y),
    ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_Z,INV_ICM20608_SCAN_GYRO_Z),
    ICM20608_CHAN(IIO_ACCEL,IIO_MOD_X,INV_ICM20608_SCAN_ACCL_X),
    ICM20608_CHAN(IIO_ACCEL,IIO_MOD_Y,INV_ICM20608_SCAN_ACCL_Y),
    ICM20608_CHAN(IIO_ACCEL,IIO_MOD_Z,INV_ICM20608_SCAN_ACCL_Z),    
};

2.3 读实现

  icm20608c_read_raw的返回值,决定读到的数据值,返回值IIO_VAL_INT为1,读到的数据按照整形返回,只有val,没有val2,返回值IIO_VAL_INT_PLUS_MICRO为2,按照val+val2/1000000反馈, 返回值IIO_VAL_INT_PLUS_NANO为2,按照val+val2/1000000000,返回值为负,读取错误。
  对于IIO_CHAN_INFO_SCALE量程数据的读取,读到寄存器值为00 01 10 11,要进行转换成实际值,转换关系有数组gyro_scale_icm20608和accel_scale_icm20608构建,最后进行输出。
  最终得到的真实数据,比如重力加速度=in_accel_z_raw(读取的值)×in_accel_scale(加速度量程)。

static const int gyro_scale_icm20608[] = {7629, 15258, 30517, 61035};
static const int accel_scale_icm20608[] = {61035, 122070, 244140, 488281};
static  unsigned  char icm20608c_read_one_reg(struct icm20608c_dev *dev,u8 reg)
{
    u8 ret;
    unsigned int data;
    ret = regmap_read(dev->regmap,reg,&data);
    return (u8)data;
}
void icm20608c_write_one_reg66(struct icm20608c_dev *dev,u8 reg,unsigned char data)
{
    regmap_write(dev->regmap,reg,data);
}
/*计算出要读寄存器的位置,根据带入的axis计算偏移地址+reg*/
static int icm20608_sensor_show(struct icm20608c_dev *dev, int reg,int axis,int *val)
{
	int ind, result;
	__be16 d;

	ind = (axis - IIO_MOD_X) * 2;
	result = regmap_bulk_read(dev->regmap, reg + ind, (u8 *)&d, 2);
	if (result)
		return -EINVAL;
	*val = (short)be16_to_cpup(&d);

	return IIO_VAL_INT;
}
static int icm20608c_read_raw(struct iio_dev *indio_dev,
			    struct iio_chan_spec const *chan,
			    int *val, int *val2, long mask)
{  
    int ret = 0;
    unsigned char regdata = 0;
    struct  icm20608c_dev *dev = iio_priv(indio_dev);
    switch(mask){
    case IIO_CHAN_INFO_RAW://原始值
        mutex_lock(&dev->lock);
        switch(chan->type){
            case IIO_TEMP:
                ret = icm20608_sensor_show(dev, ICM20_TEMP_OUT_H, IIO_MOD_X, val);//IIO_MOD_X意思是就从这个ICM20_TEMP_OUT_H开始
                break;
            case IIO_ACCEL:
                ret = icm20608_sensor_show(dev, ICM20_ACCEL_XOUT_H, chan->channel2, val);
                break;
            case IIO_ANGL_VEL:
                ret = icm20608_sensor_show(dev, ICM20_GYRO_XOUT_H, chan->channel2, val);
                break;
            default:
                ret = -EINVAL;
                break;
        }
        mutex_unlock(&dev->lock);
        return ret;
    case IIO_CHAN_INFO_CALIBBIAS://校准值      
        switch(chan->type)
        {   //对于加速度和陀螺仪的校准值,首先是校准值寄存器位置不同,其次是分为x,y,z
            case IIO_ACCEL:
                mutex_lock(&dev->lock);
                ret = icm20608_sensor_show(dev, ICM20_XA_OFFSET_H, chan->channel2, val);
                mutex_unlock(&dev->lock);  
                break;
            case IIO_ANGL_VEL:
                mutex_lock(&dev->lock);
                ret = icm20608_sensor_show(dev, ICM20_XG_OFFS_USRH, chan->channel2, val);
                mutex_unlock(&dev->lock);                 
                break;
            default:
                ret = -EINVAL;
                break; 
        } 
        break;
    case IIO_CHAN_INFO_SCALE://度量
		switch (chan->type) {
		case IIO_ANGL_VEL:
			mutex_lock(&dev->lock);
			regdata = (icm20608c_read_one_reg(dev, ICM20_GYRO_CONFIG) & 0X18) >> 3;
			*val  = 0;
			*val2 = gyro_scale_icm20608[regdata];
			mutex_unlock(&dev->lock);
			return IIO_VAL_INT_PLUS_MICRO;	/* 值为val+val2/1000000 */
		case IIO_ACCEL:
			mutex_lock(&dev->lock);
			regdata = (icm20608c_read_one_reg(dev, ICM20_ACCEL_CONFIG) & 0X18) >> 3;
			*val = 0;
			*val2 = accel_scale_icm20608[regdata];;
			mutex_unlock(&dev->lock);
			return IIO_VAL_INT_PLUS_NANO;/* 值为val+val2/1000000000 */
		case IIO_TEMP:					
			*val = ICM20608_TEMP_SCALE/ 1000000;
			*val2 = ICM20608_TEMP_SCALE % 1000000;
			return IIO_VAL_INT_PLUS_MICRO;	/* 值为val+val2/1000000 */
		default:
			return -EINVAL;
		}
		return ret;
    case IIO_CHAN_INFO_OFFSET://温度传感器offset值
        switch(chan->type)
        {
            case IIO_TEMP:
                *val =  ICM20608_TEMP_OFFSET;               
                break;
            default:
                ret = -EINVAL;     
        }
        return ret;     
    default:
        break;

    }
    printk("icm20608c_read_raw\r\n");
    return ret;

}

实现:

#include <linux/spi/spi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/unaligned/be_byteshift.h>
#include "666.h"
#define ICMC20608C_NAME  "icm20608c"
#define ICM20608_TEMP_OFFSET	     0
#define ICM20608_TEMP_SCALE		     326800000

struct icm20608c_dev {
    struct spi_device *spi;
    struct regmap *regmap;
    struct regmap_config regmap_config;
    struct mutex lock;
};
static const int gyro_scale_icm20608[] = {7629, 15258, 30517, 61035};
static const int accel_scale_icm20608[] = {61035, 122070, 244140, 488281};
#define ICM20608_CHAN(_type, _channel2, _index)                    \
	{                                                             \
		.type = _type,                                        \
		.modified = 1,                                        \
		.channel2 = _channel2,                                \
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	      \
				      BIT(IIO_CHAN_INFO_CALIBBIAS),   \
		.scan_index = _index,                                 \
		.scan_type = {                                        \
				.sign = 's',                          \
				.realbits = 16,                       \
				.storagebits = 16,                    \
				.shift = 0,                           \
				.endianness = IIO_BE,                 \
			     },                                       \
	}
enum inv_icm20608_scan {
	INV_ICM20608_SCAN_ACCL_X,
	INV_ICM20608_SCAN_ACCL_Y,
	INV_ICM20608_SCAN_ACCL_Z,
	INV_ICM20608_SCAN_TEMP,
	INV_ICM20608_SCAN_GYRO_X,
	INV_ICM20608_SCAN_GYRO_Y,
	INV_ICM20608_SCAN_GYRO_Z,
	INV_ICM20608_SCAN_TIMESTAMP,
};
static const struct iio_chan_spec icm20608_channels[] = {
    {
        .type = IIO_TEMP,
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
                            |BIT(IIO_CHAN_INFO_OFFSET)
                            |BIT(IIO_CHAN_INFO_SCALE),
        .scan_index = INV_ICM20608_SCAN_TEMP,
        .scan_type = {
                .sign = 's',
                .realbits = 16,
                .storagebits = 16,
                .shift = 0,
                .endianness = IIO_BE,
            },
    },
    ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_X,INV_ICM20608_SCAN_GYRO_X),
    ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_Y,INV_ICM20608_SCAN_GYRO_Y),
    ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_Z,INV_ICM20608_SCAN_GYRO_Z),
    ICM20608_CHAN(IIO_ACCEL,IIO_MOD_X,INV_ICM20608_SCAN_ACCL_X),
    ICM20608_CHAN(IIO_ACCEL,IIO_MOD_Y,INV_ICM20608_SCAN_ACCL_Y),
    ICM20608_CHAN(IIO_ACCEL,IIO_MOD_Z,INV_ICM20608_SCAN_ACCL_Z),    
};

static  unsigned  char icm20608c_read_one_reg(struct icm20608c_dev *dev,u8 reg)
{
    u8 ret;
    unsigned int data;
    ret = regmap_read(dev->regmap,reg,&data);
    return (u8)data;
}
void icm20608c_write_one_reg66(struct icm20608c_dev *dev,u8 reg,unsigned char data)
{
    regmap_write(dev->regmap,reg,data);
}

static int icm20608_sensor_show(struct icm20608c_dev *dev, int reg,int axis,int *val)
{
	int ind, result;
	__be16 d;

	ind = (axis - IIO_MOD_X) * 2;
	result = regmap_bulk_read(dev->regmap, reg + ind, (u8 *)&d, 2);
	if (result)
		return -EINVAL;
	*val = (short)be16_to_cpup(&d);

	return IIO_VAL_INT;
}
static int icm20608c_read_raw(struct iio_dev *indio_dev,
			    struct iio_chan_spec const *chan,
			    int *val, int *val2, long mask)
{  
    int ret = 0;
    unsigned char regdata = 0;
    struct  icm20608c_dev *dev = iio_priv(indio_dev);
    switch(mask){
    case IIO_CHAN_INFO_RAW://原始值
        mutex_lock(&dev->lock);
        switch(chan->type){
            case IIO_TEMP:
                ret = icm20608_sensor_show(dev, ICM20_TEMP_OUT_H, IIO_MOD_X, val);//IIO_MOD_X意思是就从这个ICM20_TEMP_OUT_H开始
                break;
            case IIO_ACCEL:
                ret = icm20608_sensor_show(dev, ICM20_ACCEL_XOUT_H, chan->channel2, val);
                break;
            case IIO_ANGL_VEL:
                ret = icm20608_sensor_show(dev, ICM20_GYRO_XOUT_H, chan->channel2, val);
                break;
            default:
                ret = -EINVAL;
                break;
        }
        mutex_unlock(&dev->lock);
        return ret;
    case IIO_CHAN_INFO_CALIBBIAS://校准值      
        switch(chan->type)
        {   //对于加速度和陀螺仪的校准值,首先是校准值寄存器位置不同,其次是分为x,y,z
            case IIO_ACCEL:
                mutex_lock(&dev->lock);
                ret = icm20608_sensor_show(dev, ICM20_XA_OFFSET_H, chan->channel2, val);
                mutex_unlock(&dev->lock);  
                break;
            case IIO_ANGL_VEL:
                mutex_lock(&dev->lock);
                ret = icm20608_sensor_show(dev, ICM20_XG_OFFS_USRH, chan->channel2, val);
                mutex_unlock(&dev->lock);                 
                break;
            default:
                ret = -EINVAL;
                break; 
        } 
        break;
    case IIO_CHAN_INFO_SCALE://度量
		switch (chan->type) {
		case IIO_ANGL_VEL:
			mutex_lock(&dev->lock);
			regdata = (icm20608c_read_one_reg(dev, ICM20_GYRO_CONFIG) & 0X18) >> 3;
			*val  = 0;
			*val2 = gyro_scale_icm20608[regdata];
			mutex_unlock(&dev->lock);
			return IIO_VAL_INT_PLUS_MICRO;	/* 值为val+val2/1000000 */
		case IIO_ACCEL:
			mutex_lock(&dev->lock);
			regdata = (icm20608c_read_one_reg(dev, ICM20_ACCEL_CONFIG) & 0X18) >> 3;
			*val = 0;
			*val2 = accel_scale_icm20608[regdata];;
			mutex_unlock(&dev->lock);
			return IIO_VAL_INT_PLUS_NANO;/* 值为val+val2/1000000000 */
		case IIO_TEMP:					
			*val = ICM20608_TEMP_SCALE/ 1000000;
			*val2 = ICM20608_TEMP_SCALE % 1000000;
			return IIO_VAL_INT_PLUS_MICRO;	/* 值为val+val2/1000000 */
		default:
			return -EINVAL;
		}
		return ret;
    case IIO_CHAN_INFO_OFFSET://温度传感器offset值
        switch(chan->type)
        {
            case IIO_TEMP:
                *val =  ICM20608_TEMP_OFFSET;               
                break;
            default:
                ret = -EINVAL;     
        }
        return ret;     
    default:
        break;

    }
    printk("icm20608c_read_raw\r\n");
    return ret;

}
static int icm20608c_write_raw(struct iio_dev *indio_dev,
			     struct iio_chan_spec const *chan,
			     int val, int val2, long mask)
{
    int ret = 0;
    printk("icm20608c_write_raw\r\n");
    return ret;
}
static int icm20608c_write_raw_get_fmt(struct iio_dev *indio_dev,
			       struct iio_chan_spec const *chan,
			       long mask)
{
    int ret = 0;
    return ret;
}

/*文件操作函数*/
static struct iio_info icm20608c_info = {
    .read_raw = icm20608c_read_raw,
    .write_raw = icm20608c_write_raw,
    .write_raw_get_fmt = &icm20608c_write_raw_get_fmt,
};

/*寄存器初始化*/
static void icm20608c_reg_init( struct icm20608c_dev *dev)
{
    u8 value = 0,ret = 0;
    icm20608c_write_one_reg66(dev, ICM20_PWR_MGMT_1,0x80);
    mdelay(50);
    icm20608c_write_one_reg66(dev, ICM20_PWR_MGMT_1,0x01);
    mdelay(50);
    ret = icm20608c_read_one_reg(dev, ICM20_PWR_MGMT_1);
    printk("ICM20_PWR_MGMT_1 = %X\r\n", ret);
    value = icm20608c_read_one_reg(dev, ICM20_WHO_AM_I);
    printk("ICM20608 ID = %X\r\n", value);
    icm20608c_write_one_reg66(dev, ICM20_SMPLRT_DIV, 0x00); 
    icm20608c_write_one_reg66(dev, ICM20_GYRO_CONFIG, 0x18); 
    icm20608c_write_one_reg66(dev, ICM20_ACCEL_CONFIG, 0x18); 
    icm20608c_write_one_reg66(dev, ICM20_CONFIG, 0x04); 
    icm20608c_write_one_reg66(dev, ICM20_ACCEL_CONFIG2, 0x04);
    icm20608c_write_one_reg66(dev, ICM20_PWR_MGMT_2, 0x00); 
    icm20608c_write_one_reg66(dev, ICM20_LP_MODE_CFG, 0x00); 
    icm20608c_write_one_reg66(dev, ICM20_FIFO_EN, 0x00); 
};
const struct of_device_id of_icm20608c_table[]={
    {.compatible = "wyt,icm20608"},
    {}
};
const struct spi_device_id icm20608c_table[]={
    {"icm20608c",0},
    { }
};
/*完成设备号注册 节点注册 spi设备初始化*/
static int icm20608c_probe(struct spi_device *spi)
{
    int ret = 0;
    struct icm20608c_dev *dev;
    struct iio_dev *indio_dev;

    /*1.iio_dev申请内存*/
    indio_dev = devm_iio_device_alloc(&spi->dev,sizeof(*dev));
    if(!indio_dev)
    {
        return ret;
    }
    /*2.获取icm20608c_dev结构体地址*/
    dev = iio_priv(indio_dev);
    dev->spi = spi;
    spi_set_drvdata(spi,indio_dev);
    mutex_init(&dev->lock);
    /*3.初始化iio_dev成员变量*/
    indio_dev->dev.parent = &spi->dev;
    indio_dev->info = &icm20608c_info;
    indio_dev->name = ICMC20608C_NAME;
    indio_dev->modes = INDIO_DIRECT_MODE;
    indio_dev->channels = icm20608_channels;
    indio_dev->num_channels = ARRAY_SIZE(icm20608_channels);
    /*4.注册iio_dev*/
    ret = iio_device_register(indio_dev);
    if(ret<0)
    {
        return ret;
    }
    /*5.初始化regmap_config并且注册*/
    dev->regmap_config.reg_bits = 8;//寄存器大小为8bit
    dev->regmap_config.val_bits = 8;//寄存器数据值8bit
    //dev.regmap_config.read_flag_mask = 0x80;//读掩码
    dev->regmap = regmap_init_spi(spi,&dev->regmap_config);
    if(IS_ERR(dev->regmap))
    {
        goto regmap_error;
        return PTR_ERR(dev->regmap);
    }
    /*6.初始化SPI设备*/
    spi->mode = SPI_MODE_0;//时钟模式
    spi_setup(spi);
    /*7.寄存器初始化内容*/
    icm20608c_reg_init(dev);
    printk("probe success \r\n");
    return 0;
regmap_error:
    iio_device_unregister(indio_dev);    
    return ret;
}
static int icm20608c_remove(struct spi_device *spi)
{
    /*这段代码,为了获取icm20608c_dev的地址和iio_dev地址,以便进行解除*/
    struct icm20608c_dev *dev;
    struct iio_dev *indio_dev;
    indio_dev = spi_get_drvdata(spi);
    dev = iio_priv(indio_dev);
    /*删除regmap,注销iio_dev*/
    regmap_exit(dev->regmap);
    iio_device_unregister(indio_dev);    
    printk("remove success \r\n");
    return 0;
}
static struct spi_driver icm20608c_driver={
    .probe = icm20608c_probe,
    .remove = icm20608c_remove,
    .driver = {
        .name = "icm20608c_spi_driver",
        .of_match_table = of_icm20608c_table,
    },
    .id_table = icm20608c_table,

};
static int __init  icm20608c_init(void)
{
    return spi_register_driver(&icm20608c_driver);
}
static void __exit icm20608c_exit(void)
{
    spi_unregister_driver(&icm20608c_driver);
}
/*驱动入口*/
module_init(icm20608c_init);
module_exit(icm20608c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WYT");


over

驱动部分总体上都结束了,基本上是字符设备的驱动编写,块设备写一个RAM的,网络设备简单看了看框架,还是要干点项目,后面从100节视频往后,就开始不想学了,后面的章节都是搭建一些环境和软件,也不用动手敲了,这两天把这个会议整完,把操作系统学完。五月开始,找点项目干干,感觉还是没学到什么东西啊!!!!

更多推荐