在调试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等方式,这里不再赘述。