linux驱动IS_ERR_VALUE、IS_ERR

一、关于Linux内核非法地址(网上搜加自己总结)

  1、对任何一个指针,必然有三种情况:一种是有效指针,一种是NULL,空指针,一种是错误指针,或者说无效指针

  2、32位CPU,内核空间占用虚拟地址(0xc0000000~0xffffffff)3~4G虚拟地址(线性地址?)

  3、其中最后一个page(假设4K大小)的地址为(0xfffff000~0xffffffff)为预留地址,指向这个地址的指针都是非法指针

二、错误码跟非法指针啥关系

  1、<asm-generic/errno-base.h> 中规定了通用错误码1-34 <linux/err.h>中定义了 #define MAX_ERRNO  4095 最大错误码为4095

  2、插一段代码,看内核如何返回错误码。可以看出,在错误码前面加上一个负号就变成了返回值

  1. /* Check type and command number */

  2. if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)

  3. return -ENOTTY;

3、继续以-ENOTTY为例,ENOTTY的值为25,-ENOTTY的值是多少呢。负数在计算机中以二进制补码形式存储。

      补码=~源码+1(源码按位取反后加1)

      那么-25的补码就为 0xFFFFFFE7。仔细看一下这个值,在0xfffff000~0xffffffff之间,所以它就是个错误指针。

      所以错误码取负值后在内存中存储的值就是内核的错误指针。

三、IS_ERR_VALUE宏定义分析

#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)

  1、unlikely宏啥意思现还没深入研究,好像是高速编译器哪个不可能发生,用来优化代码,先不管。

  2、(unsigned long)-MAX_ERRNO的值为 0xFFFFF001,也就是大于等于0xFFFFF001的指针为非法指针。

  3、根据上面的分析,负的错误码与非法指针等价,IS_ERR_VALUE宏既可以用来判断返回错误码(负值)的函数,又可以用来判断返回错误指针的函数。

四、ERR_PTR宏定义分析

 
  1. static inline void * __must_check ERR_PTR(long error)

  2. {

  3. return (void *) error;

  4. }

  1、字面上来看,把一个long型error转换为指针类型,猜测一下是把负的错误码转换为 void *

  2、看别人怎么用。可以看出,ERR_PTR宏把负的错误码转换为 void *指针。在返回指针的函数中可以这样用,返回一个非法地址,这个跟用户态错误就返回空指针是不同的

 
  1. static struct ac97c_platform_data *atmel_ac97c_probe_dt(struct device *dev)

  2. {

  3. struct ac97c_platform_data *pdata;

  4. struct device_node *node = dev->of_node;

  5.  
  6. if (!node) {

  7. dev_err(dev, "Device does not have associated DT data\n");

  8. return ERR_PTR(-EINVAL);

  9. }

  10.  
  11. pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);

  12. if (!pdata)

  13. return ERR_PTR(-ENOMEM);

  14.  
  15. pdata->reset_pin = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);

  16.  
  17. return pdata;

  18. }

五、PTR_ERR宏定义分析

 
  1. static inline long __must_check PTR_ERR(__force const void *ptr)

  2. {

  3. return (long) ptr;

  4. }

1、字面上来看把指针转换为整数类型,合理猜测是把非法指针转换为错误码

2、看别人怎么用。IS_ERR宏就是调用了IS_ERR_VALUE,后面分析。可以看出先判断一下指针是不是非法的,如果是非法的,则将错误指针通过PTR_ERR转换为错误码,并返回一个整形数值。

 
  1. static int act8945a_i2c_probe(struct i2c_client *i2c,

  2. const struct i2c_device_id *id)

  3. {

  4. int ret;

  5. struct regmap *regmap;

  6.  
  7. regmap = devm_regmap_init_i2c(i2c, &act8945a_regmap_config);

  8. if (IS_ERR(regmap)) {

  9. ret = PTR_ERR(regmap);

  10. dev_err(&i2c->dev, "regmap init failed: %d\n", ret);

  11. return ret;

  12. }

  13.  
  14. i2c_set_clientdata(i2c, regmap);

  15.  
  16. ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_NONE,

  17. act8945a_devs, ARRAY_SIZE(act8945a_devs),

  18. NULL, 0, NULL);

  19. if (ret) {

  20. dev_err(&i2c->dev, "Failed to add sub devices\n");

  21. return ret;

  22. }

  23.  
  24. return 0;

  25. }

六、PTR_ERR宏定义分析

 
  1. static inline bool __must_check IS_ERR(__force const void *ptr)

  2. {

  3. return IS_ERR_VALUE((unsigned long)ptr);

  4. }

  这个宏就是调用了IS_ERR_VALUE,来判断指针是否非法。

 

附上 LDD3 的说明:

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
通过`devm_regmap_init_i2c`函数和`regmap_config`结构体,可以很方便地实现I2C读写操作。 以下是一个简单的I2C读寄存器的例子: ```c #include <linux/i2c.h> #include <linux/regmap.h> static const struct regmap_config my_regmap_config = { .name = "my_device_regmap", .reg_bits = 8, .val_bits = 8, .max_register = 0xFF, .cache_type = REGCACHE_NONE, }; static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; struct device *dev = &client->dev; struct regmap *regmap; regmap = devm_regmap_init_i2c(client, &my_regmap_config); if (IS_ERR(regmap)) { dev_err(dev, "Failed to initialize regmap: %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } // 读取0x10号寄存器 u8 reg = 0x10; u8 val; ret = regmap_read(regmap, reg, &val); if (ret) { dev_err(dev, "Failed to read reg 0x%x: %d\n", reg, ret); return ret; } dev_info(dev, "Reg 0x%x value: 0x%x\n", reg, val); return 0; } ``` 在这个例子中,`my_i2c_probe`函数中调用`devm_regmap_init_i2c`函数初始化了一个I2C设备,并且创建了一个名为"my_device_regmap"的regmap结构体。然后,通过`regmap_read`函数来读取0x10号寄存器的值,将其存储在`val`变量中。 类似地,可以通过`regmap_write`函数来写寄存器的值,如下所示: ```c // 将0x20号寄存器写入值0xAB u8 reg = 0x20; u8 val = 0xAB; ret = regmap_write(regmap, reg, val); if (ret) { dev_err(dev, "Failed to write reg 0x%x: %d\n", reg, ret); return ret; } ``` 在这个例子中,通过`regmap_write`函数将0x20号寄存器的值设置为0xAB。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值