目前在kernel驱动代码中,都不再建议直接使用printk直接添加打印信息,而是使用dev_info,dev_dbg,dev_err
之类的函数代替。
注意:这三个函数声明于linux/include/linux/device.h
使用上述三个打印函数时打印信息格式如下
驱动模块的名字: + 具体的信息
如:在运行的linux系统设备上,插入鼠标,就会打印出鼠标的相关信息;
[ 402.134068] input: USB Optical Mouse as /devices/soc0/soc/2100000.aips-bus/2184000.usb/ci_hdrc.0/usb1/1-1/1-1:1.0/0003:0461:4D81.0003/input/input3
[ 402.149618] hid-generic 0003:0461:4D81.0003: input: USB HID v1.11 Mouse [USB Optical Mouse] on usb-ci_hdrc.0-1/input0
"input: " 是鼠标插入式,内核检测到,鼠标是输入设备,于是就把将鼠标与输入模块的驱动。
“hid-generic 0003:0461:4D81.0003” 是鼠标匹配的驱动是输入模块的hid-generic驱动。
其使用如下:
dev_info(dev, “%s[%d] \n”, __func__, __LINE__);
这是第一个参数不同,后两个就是printk的参数相同,格式也相同。
第一个参数dev: 就是将这个设备的模块的名字等信息相同, 最终还是调用printk打印出来.
通常,dev第一个参数的打信息,是用两个函数从dev中获取信息。
const char *dev_driver_string(const struct device *dev)
{
struct device_driver *drv;
/* dev->driver can change to NULL underneath us because of unbinding,
* so be careful about accessing it. dev->bus and dev->class should
* never change once they are set, so they don't need special care.
*/
drv = ACCESS_ONCE(dev->driver);
return drv ? drv->name :
(dev->bus ? dev->bus->name :
(dev->class ? dev->class->name : ""));
}
调用这个函数可以输出 "hid-generic"
static inline const char *dev_name(const struct device *dev)
{
/* Use the init name until the kobject becomes available */
if (dev->init_name)
return dev->init_name;
return kobject_name(&dev->kobj);
}
dev_name(&intf->dev)对应着DEV的四元组信息,可以用这个信息,来判断设备的具体位置,然后根据该信息,给设备固定名字。
调用这个函数,可以输出" 0003: ......"
dev_driver_string() / dev_name() 是内核调试打印信息中常用的输出函数
函数dev_err通过同一文件(drivers / base / core.c)中的define_dev_printk_level宏实现.
static void __dev_printk(const char *level, const struct device *dev,
struct va_format *vaf)
{
if (dev)
dev_printk_emit(level[1] - '0', dev, "%s %s: %pV",
dev_driver_string(dev), dev_name(dev), vaf);
else
printk("%s(NULL device *): %pV", level, vaf);
}
void dev_printk(const char *level, const struct device *dev,
const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
__dev_printk(level, dev, &vaf);
va_end(args);
}
EXPORT_SYMBOL(dev_printk);
#define define_dev_printk_level(func, kern_level) \
void func(const struct device *dev, const char *fmt, ...) \
{ \
struct va_format vaf; \
va_list args; \
\
va_start(args, fmt); \
\
vaf.fmt = fmt; \
vaf.va = &args; \
\
__dev_printk(kern_level, dev, &vaf); \
\
va_end(args); \
} \
EXPORT_SYMBOL(func);
define_dev_printk_level(dev_emerg, KERN_EMERG);
define_dev_printk_level(dev_alert, KERN_ALERT);
define_dev_printk_level(dev_crit, KERN_CRIT);
define_dev_printk_level(dev_err, KERN_ERR);
define_dev_printk_level(dev_warn, KERN_WARNING);
define_dev_printk_level(dev_notice, KERN_NOTICE);
define_dev_printk_level(_dev_info, KERN_INFO);
#endif
这里利用宏 define_dev_printk_level 定义了 dev_emerg/dev_alert/dev_crit/dev_err/dev_warn/dev_notice/_dev_info 等几个函数。其实这几个函数归根结底都是函数调用__dev_printk。
另外在 include/linux/device.h 中
extern __printf(2, 3)
int dev_emerg(const struct device *dev, const char *fmt, ...);
extern __printf(2, 3)
int dev_alert(const struct device *dev, const char *fmt, ...);
extern __printf(2, 3)
int dev_crit(const struct device *dev, const char *fmt, ...);
extern __printf(2, 3)
int dev_err(const struct device *dev, const char *fmt, ...);
extern __printf(2, 3)
int dev_warn(const struct device *dev, const char *fmt, ...);
extern __printf(2, 3)
int dev_notice(const struct device *dev, const char *fmt, ...);
extern __printf(2, 3)
int _dev_info(const struct device *dev, const char *fmt, ...);
__printf(2, 3) 的定义见: include/linux/compiler-gcc.h
#define __printf(a, b) __attribute__((format(printf, a, b)))
函数 dev_info 和 dev_dbg的实现
#define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg)
#if defined(CONFIG_DYNAMIC_DEBUG)
#define dev_dbg(dev, format, ...) \
do { \
dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \
} while (0)
#elif defined(DEBUG)
#define dev_dbg(dev, format, arg...) \
dev_printk(KERN_DEBUG, dev, format, ##arg)
#else
#define dev_dbg(dev, format, arg...) \
({ \
if (0) \
dev_printk(KERN_DEBUG, dev, format, ##arg); \
})
#endif