在调试linux驱动的时候,经常需要用户层和内核态进行交互,比较常用的方法是ioctl和文件节点,这里介绍一下在设备驱动增加文件节点的常见方法。
(1)module_param方式
可以在驱动使用函数module_param来设置参数,本意就是设置加载驱动的时候可以传入哪些参数,代码示例如下:
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "debug level (0-3)");
设置上述参数之后,会在对应的module下面生成文件节点,例如lt6911uxc:
/sys/module/lt6911uxc/parameters/debug
(2)proc文件节点
可以在/proc/目录下生成对应的文件节点,示例如下:
void lt7911uxc_init_procfs(struct lt7911uxc *lt7911uxc)
{
struct proc_dir_entry *parent;
struct device *dev = <7911uxc->i2c_client->dev;
g_lt7911uxc = lt7911uxc;
g_lt7911uxc->crc = LT7911UXC_CRC;
parent = proc_mkdir("lt7911uxc", NULL);
if (!parent) {
dev_err(dev, "lt7911uxc init procfs mkdir fail!");
return;
}
proc_create("crc-set", S_IWUSR | S_IWGRP, parent, &set_crc_fops);
dev_info(dev, "-- lt7911uxc_init_procfs --");
}
static ssize_t lt7911uxc_crc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
u8 data[3]= { 0xff };
const size_t size = sizeof(data);
struct device *dev = &g_lt7911uxc->i2c_client->dev;
unsigned long val;
dev_info(dev, "-- lt7911uxc crc write start --\n");
if (count > size)
return -EINVAL;
if (copy_from_user(data, buffer, count))
return -EFAULT;
val = simple_strtoul(data, NULL, 16);
g_lt7911uxc->crc = val;
dev_info(dev, "-- lt7911uxc_crc_write: %x --", g_lt7911uxc->crc);
return count;
}
static ssize_t lt7911uxc_crc_read(struct file *fd,
char __user *buf_, size_t size, loff_t *offset)
{
u16 ver = 0;
u8 out[7] = {0};// one byte max value in hex is "255".
ssize_t len = 0;
ver = g_lt7911uxc->crc;
len = sizeof(out) - *offset;
if (len < 0)
len = 0;
snprintf(out, sizeof(out), "%x\n", ver);
if (copy_to_user(buf_, out, len) ) {
pr_err("copy to user failed");
return -EFAULT;
}
*offset += len;
pr_info("-- fw_version_read --, len: %zd\n", len);
return len;
}
static const struct proc_ops set_crc_fops = {
.proc_write = lt7911uxc_crc_write,
.proc_read = lt7911uxc_crc_read,
};
通过函数proc_mkdir在/proc/目录下创建一个目录,通过proc_create函数创建文件节点,再实现对应的节点的读写函数即可。
例如上述例子,可以通过如下命令操作:
cat /proc/lt7911uxc/crc-set
echo 1 > /proc/lt7911uxc/crc-set
(3)class文件节点
创建class文件节点。如下示例,在sys/class的目录下创建max96714的目录,在max96714目录下再创建max96714_state目录,后面生成的属性都会在这个目录下。
static int max96714_create_class_attr(struct max96714 *max96714)
{
int ret = 0;
struct device *dev = &max96714->client->dev;
max96714->class = class_create(THIS_MODULE, "max96714");
if (IS_ERR(max96714->class)) {
ret = -ENOMEM;
dev_err(dev, "failed to create max96714 class!\n");
return ret;
}
max96714->classdev = device_create_with_groups(max96714->class, dev,
MKDEV(0, 0), max96714,
max96714_groups, "max96714_state");
if (IS_ERR(max96714->classdev)) {
ret = PTR_ERR(max96714->classdev);
dev_err(dev, "Failed to create device\n");
goto err;
}
ret = devm_add_action_or_reset(dev, max96714_unregister_class_device, max96714);
if (ret)
dev_err(dev, "device unregister max96714 class failed!\n");
return ret;
err:
class_destroy(max96714->class);
return ret;
}
static void max96714_remove_class_attr(struct max96714 *max96714)
{
class_destroy(max96714->class);
}
static void max96714_unregister_class_device(void *data)
{
struct max96714 *max96714 = data;
struct device *dev = max96714->classdev;
device_unregister(dev);
}
static ssize_t mcu_rise_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max96714 *max96714 = dev_get_drvdata(dev);
u8 checksum = 0;
int ret;
u8 rise_val = 0;
...
return snprintf(buf, PAGE_SIZE, "%x\n", rise_val);
}
static ssize_t mcu_rise_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct max96714 *max96714 = dev_get_drvdata(dev);
uint8_t value, value_l;
u8 checksum = 0;
int ret;
if (!max96714)
return -EINVAL;
sscanf(buf, "%d %d", &value, &value_l);
...
return count;
}
static DEVICE_ATTR_RW(mcu_rise);
static struct attribute *max96714_attrs[] = {
&dev_attr_mcu_rise.attr,
NULL
};
ATTRIBUTE_GROUPS(max96714);
(4)其他方式
其他的创建方式还有sysfs_create_files,device_create_file等方式,这里不再赘述。