当前位置: 首页 > news >正文

Linux驱动开发-①regmap②IIO子系统

Linux驱动开发-IIO驱动

  • 一,regmap
  • 二,IIO子系统
    • 2.1初始化相关工作
    • 2.2 通道
    • 2.3 读实现
  • over

一,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;//寄存器大小为8biticm20608c.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设备关联起来,创建出regmapif(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;//寄存器大小为8bitdev->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,zcase 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		     326800000struct 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,zcase 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;//寄存器大小为8bitdev->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节视频往后,就开始不想学了,后面的章节都是搭建一些环境和软件,也不用动手敲了,这两天把这个会议整完,把操作系统学完。五月开始,找点项目干干,感觉还是没学到什么东西啊!!!!

相关文章:

  • Spring Boot 项目中发布流式接口支持实时数据向客户端推送
  • 【KWDB创作者计划】_KwDB2.2.0深度实践:从存储引擎到物联网场景的多模数据库实战
  • XSS之同源、跨域、内容安全策略
  • C语言——数组
  • 【网络技术_域名解析DNS】一、DNS 基础剖析及其原理
  • [轻量化超分]CAMixerSR: Only Details Need More “Attention“
  • HTML5好看的水果蔬菜在线商城网站源码系列模板5
  • 有什么工具可以在家连接到公司内网?局域网址提供异地公网访问的那些常用方法
  • CentOS系统-超详细的Kubernetes集群搭建教程(kubernetes:1.28.2)
  • Rust生命周期、文件与IO
  • 20.3 使用技巧3
  • Qwen2.5-VL视觉大语言模型复现过程,没碰到什么坑
  • 【AI量化第24篇】KhQuant 策略框架深度解析:让策略开发回归本质——基于miniQMT的量化交易回测系统开发实记
  • Go语言入门到入土——三、处理并返回异常
  • Python爬虫之线程、进程、协程详解
  • Python 实现日志备份守护进程
  • JavaScript模块化开发:CommonJS、AMD到ES模块
  • AUTOSAR图解==>AUTOSAR_SWS_CryptoDriver
  • blender里面的材质列表
  • JavaEE——线程安全
  • 大四本科生已发14篇SCI论文?学校工作人员:已记录汇报
  • 上海发布大风黄警:预计未来24小时内将出现8-10级大风
  • 习近平离京赴莫斯科对俄罗斯进行国事访问并出席纪念苏联伟大卫国战争胜利80周年庆典
  • 外交部回应西班牙未来外交战略:愿与之一道继续深化开放合作
  • 央行、证监会:科技创新债券含公司债券、企业债券、非金融企业债务融资工具等
  • 大学2025丨专访清华教授沈阳:建议年轻人每天投入4小时以上与AI互动