参考:
《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)
完。