如何为FPGA的AXI-slave设备寄存器批量创建sysfs属性函数?
Great question! When dealing with large numbers of identical sysfs attributes for memory-mapped registers (like your AXI slave registers), preprocessor macros are the standard Linux kernel way to eliminate redundant code. They let you generate all the repetitive _show/_store functions and device attribute definitions in a single line per register.
Step 1: Define Reusable Macros
First, create macros that encapsulate the common logic for read-only and read-write registers. These macros will generate the full function definitions and DEVICE_ATTR declarations automatically.
For Read-Only Registers
#define DEFINE_READ_REG_ATTR(name, reg_offset) \ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) { \ struct axi_register_driver *drv = dev_get_drvdata(dev); \ u32 reg_val = ioread32(drv->reg_base + (reg_offset)); \ return sprintf(buf, "0x%x\n", reg_val); \ } \ static DEVICE_ATTR_RO(name)
For Read-Write Registers
#define DEFINE_RW_REG_ATTR(name, reg_offset) \ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) { \ struct axi_register_driver *drv = dev_get_drvdata(dev); \ u32 reg_val = ioread32(drv->reg_base + (reg_offset)); \ return sprintf(buf, "0x%x\n", reg_val); \ } \ static ssize_t name##_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { \ struct axi_register_driver *drv = dev_get_drvdata(dev); \ u32 val; \ /* Use base 0 to support both decimal and hex input (e.g., "123" or "0x7b") */ \ if (kstrtou32(buf, 0, &val) != 0) \ return -EINVAL; \ iowrite32(val, drv->reg_base + (reg_offset)); \ return count; \ } \ static DEVICE_ATTR_RW(name)
Step 2: Generate Attributes for Your Registers
Now you can create all 32 registers with just one line each. For example:
// Read-only registers DEFINE_READ_REG_ATTR(readreg0, 0); DEFINE_READ_REG_ATTR(readreg1, OFF_REG1); // ... add 30 more read-only registers here ... // Read-write registers DEFINE_RW_REG_ATTR(writereg0, WRITE_OFFSET + 0); // ... add other RW registers here ...
Step 3: Group Attributes for Easy Registration
To add these attributes to your device, create an attribute array and group:
static struct attribute *axi_reg_attrs[] = { &dev_attr_readreg0.attr, &dev_attr_readreg1.attr, &dev_attr_writereg0.attr, // ... add pointers to all your generated attributes ... NULL, /* Terminate the array */ }; static const struct attribute_group axi_reg_attr_group = { .attrs = axi_reg_attrs, };
Then in your driver's probe function, register the group with:
int ret = device_add_groups(dev, &axi_reg_attr_group); if (ret) { dev_err(dev, "Failed to add sysfs attributes\n"); return ret; }
Key Notes
- The
##in the macros is the token pasting operator—it combines thenameparameter with_show/_storeto create the function names (e.g.,readreg0becomesreadreg0_show). - Using
kstrtou32(buf, 0, &val)instead of base 10 lets users input values in decimal or hexadecimal (more flexible for register access). - All generated code is resolved at compile time, so there's no runtime overhead compared to writing each function manually.
This approach keeps your code clean, maintainable, and avoids the error-prone process of copying/pasting 32 identical function definitions.
内容的提问来源于stack exchange,提问作者Franz Forstmayr

