Linux驱动开发——指针和错误值

参考:

《Linux设备驱动程序》第三版 P294

  许多内部的内核函数返回一个指针值给调用者,而这些函数中很多可能会失败。在大部分情况下,失败是通过返回一个NULL指针值来表示的。这种技巧有作用,但是它不能传递问题的确切性质。某些接口确实需要返回一个实际的错误编码,以使调用者可以根据实际出错的情况做出正确的决策。

  许多内核接口通过把错误值编码到一个指针值中来返回错误信息。这种函数必须小心使用,因为他们的返回值不能简单地和NULL比较。为了帮助创建和使用这种类型的接口,<linux/err.h>中提供了一小组函数。

  返回指针类型的函数可以通过如下函数返回一个错误值:

    void *ERR_PTR(long err);

  这里error是通常的负的错误编码。调用者可以使用IS_ERR来检查所返回的指针是否是一个错误编码:

    long IS_ERR(const void *ptr);

  如果需要实际的错误编码,可以通过如下函数把它提取出来:

    long PTR_ERR(const void *ptr);

  应该使用IS_ERR对某值返回真值时才对该值使用PTR_ERR,因为任何其他值都是有效指针。

补充:

在include/linux/err.h中定义如下几个宏:

1、void * ERR_PTR(long error):将错误编码转为指针

2、long PTR_ERR(const void *ptr):将指针转为错误编码

3、bool IS_ERR(const void *ptr):检查返回指针是否为一个错误编码,如果返回真,表示确实发生了错误

4、bool IS_ERR_OR_NULL(const void *ptr):检查返回的指针是否为NULL或是否为一个错误编码

5、int PTR_ERR_OR_ZERO(const void *ptr):将返回的指针转为0或者错误编码

6、void * ERR_CAST(const void *ptr):将const void *转为void *,防止编译报错

 1 #define MAX_ERRNO    4095
 2 
 3 #define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)
 4 
 5 static inline void * __must_check ERR_PTR(long error)
 6 {
 7     return (void *) error;
 8 }
 9 
10 static inline long __must_check PTR_ERR(__force const void *ptr)
11 {
12     return (long) ptr;
13 }
14 
15 static inline bool __must_check IS_ERR(__force const void *ptr)
16 {
17     return IS_ERR_VALUE((unsigned long)ptr);
18 }
19 
20 static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr)
21 {
22     return unlikely(!ptr) || IS_ERR_VALUE((unsigned long)ptr);
23 }
24 
25 /**
26  * ERR_CAST - Explicitly cast an error-valued pointer to another pointer type
27  * @ptr: The pointer to cast.
28  *
29  * Explicitly cast an error-valued pointer to another pointer type in such a
30  * way as to make it clear that's what's going on.
31  */
32 static inline void * __must_check ERR_CAST(__force const void *ptr)
33 {
34     /* cast away the const */
35     return (void *) ptr;
36 }
37 
38 static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr)
39 {
40     if (IS_ERR(ptr))
41         return PTR_ERR(ptr);
42     else
43         return 0;
44 }

示例:

1、比如函数_devm_regulator_get用于获得指定的regulator指针:

 1 static struct regulator *_devm_regulator_get(struct device *dev, const char *id,
 2                          int get_type)
 3 {
 4     struct regulator **ptr, *regulator;
 5 
 6     ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL);
 7     if (!ptr)
 8         return ERR_PTR(-ENOMEM);
 9 
10     switch (get_type) {
11     case NORMAL_GET:
12         regulator = regulator_get(dev, id);
13         break;
14     case EXCLUSIVE_GET:
15         regulator = regulator_get_exclusive(dev, id);
16         break;
17     case OPTIONAL_GET:
18         regulator = regulator_get_optional(dev, id);
19         break;
20     default:
21         regulator = ERR_PTR(-EINVAL);
22     }
23 
24     if (!IS_ERR(regulator)) {
25         *ptr = regulator;
26         devres_add(dev, ptr);
27     } else {
28         devres_free(ptr);
29     }
30 
31     return regulator;
32 }

第7行返回NULL,表示内存分配失败,但是由于该函数需要返回的是指针类型,所以我们用ERR_PTR对-ENOMEM进行包装

第21行,表示传入的参数不合法,此时用ERR_PTR对-EINVAL进行包装

下面是调用devm_regulator_get的地方:

1     case snd_soc_dapm_regulator_supply:
2         w->regulator = devm_regulator_get(dapm->dev, w->name);
3         if (IS_ERR(w->regulator)) {
4             ret = PTR_ERR(w->regulator);
5             dev_err(dapm->dev, "ASoC: Failed to request %s: %d
",
6                 w->name, ret);
7             return NULL; 
8         }

第3行,如果IS_ERR返回指针确实是一个错误编码对应的指针,第4行就会用PTR_ERR将该指针转为一个错误编码给ret,后面可以根据需要处理该ret。

第7行,除了使用return NULL之外,还可以使用ERR_CAST(w->regulator)

 

完。

原文地址:https://www.cnblogs.com/pengdonglin137/p/7596979.html